diff --git a/CHANGELOG.md b/CHANGELOG.md index d638bf8a..6f89c946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,18 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) -## [Version 1.3.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.3.0) (2021-12-2) +## [Version 1.4.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.4.0) (2021-12-15) + +## What's Changed +* Added Status Messages to logs for discoverDevices request. +* Added Cached Status to IR device, Status will be saved to accessory context and restored on restart. +* Added Option `Offline as Off` to be able set the device as off, if API reports offline. +* Removed Meter Unit Config Option as it was confusing and probably never used. +* Housekeeping and updated dependencies. + +**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v1.3.0...v1.4.0 + +## [Version 1.3.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.3.0) (2021-12-02) ## What's Changed * Added Adpative Lighting to Color Bulb diff --git a/config.schema.json b/config.schema.json index 2626dc88..cf238f96 100644 --- a/config.schema.json +++ b/config.schema.json @@ -58,6 +58,14 @@ "hide_device": { "title": "Hide Device", "type": "boolean", + "description": "If true then devices that are reported as offline will be shown as off in HomeKit.", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", "condition": { "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId);" } @@ -196,27 +204,6 @@ "condition": { "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Meter' && model.options.devices[arrayIndices].deviceId);" } - }, - "unit": { - "title": "Convert your Meter's Temperature to Celsius or Fahrenheit", - "type": "number", - "oneOf": [ - { - "title": "Celsius", - "enum": [ - 0 - ] - }, - { - "title": "Fahrenheit", - "enum": [ - 1 - ] - } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Meter' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].meter.hide_temperature);" - } } } }, @@ -561,14 +548,14 @@ "type": "object", "properties": { "swing_mode": { - "title": "Enable Swing Mode by Device ID", + "title": "Enable Swing Mode", "type": "boolean", "condition": { "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" } }, "rotation_speed": { - "title": "Enable Rotation Speed by Device ID", + "title": "Enable Rotation Speed", "type": "boolean", "condition": { "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" @@ -643,7 +630,7 @@ "type": "string", "placeholder": "Off", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && !model.options.irdevices[arrayIndices].other.deviceType);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && model.options.irdevices[arrayIndices].other.deviceType);" } } } @@ -716,13 +703,13 @@ "options.devices[].configDeviceName", "options.devices[].deviceId", "options.devices[].hide_device", + "options.devices[].offline", "options.devices[].ble", "options.devices[].configDeviceType", "options.devices[].bot.mode", "options.devices[].bot.deviceType", "options.devices[].meter.hide_temperature", "options.devices[].meter.hide_humidity", - "options.devices[].meter.unit", "options.devices[].humidifier.set_minStep", "options.devices[].humidifier.hide_temperature", "options.devices[].curtain.set_minStep", diff --git a/homebridge-ui/public/index.html b/homebridge-ui/public/index.html index 6654603f..7461d502 100644 --- a/homebridge-ui/public/index.html +++ b/homebridge-ui/public/index.html @@ -64,7 +64,10 @@ Firmware Version - + + Device Type + + Connection Type @@ -173,6 +176,7 @@
Help/About
document.getElementById('deviceID').innerHTML = context.deviceID document.getElementById('model').innerHTML = context.model document.getElementById('firmwareRevision').innerHTML = context.firmwareRevision || 'N/A' + document.getElementById('deviceType').innerHTML = context.deviceType document.getElementById('connectionType').innerHTML = context.connectionType document.getElementById('deviceTable').style.display = 'inline-table' homebridge.hideSpinner() diff --git a/package-lock.json b/package-lock.json index e0fdb81e..4d3dfd87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@switchbot/homebridge-switchbot", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@switchbot/homebridge-switchbot", - "version": "1.3.0", + "version": "1.4.0", "funding": [ { "type": "Paypal", @@ -21,21 +21,21 @@ "rxjs": "^7.4.0" }, "devDependencies": { - "@types/node": "^16.11.11", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "eslint": "^8.3.0", + "@types/node": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", + "eslint": "^8.4.1", "homebridge": "^1.3.8", "nodemon": "^2.0.15", - "npm-check-updates": "^12.0.2", + "npm-check-updates": "^12.0.5", "rimraf": "^3.0.2", "ts-node": "^10.4.0", - "typescript": "^4.5.2", + "typescript": "^4.5.4", "typescript-axios-wb": "^1.0.3" }, "engines": { "homebridge": "^1.3.8", - "node": "^14.18.1 || ^16.13.0" + "node": "^14.18.2 || ^16.13.1" } }, "node_modules/@abandonware/bluetooth-hci-socket": { @@ -103,14 +103,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", + "espree": "^9.2.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", @@ -158,12 +158,12 @@ "integrity": "sha512-axFX7lN2Yd7bz/6SlD7dzq5sY/N9+XYuLHPukuiyHQRDtNMRL1uDqJhOx6RVaN2tYDHB75h7YxRQWP7U44Mgzg==" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" }, @@ -239,13 +239,16 @@ } }, "node_modules/@npmcli/fs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", - "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", "dev": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" } }, "node_modules/@npmcli/git": { @@ -405,19 +408,19 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", - "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz", + "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", + "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.5.0", - "@typescript-eslint/scope-manager": "5.5.0", + "@typescript-eslint/experimental-utils": "5.7.0", + "@typescript-eslint/scope-manager": "5.7.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -443,15 +446,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz", - "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -467,14 +470,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz", - "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", + "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "debug": "^4.3.2" }, "engines": { @@ -494,13 +497,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz", - "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -511,9 +514,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz", - "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -524,13 +527,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz", - "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -551,12 +554,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz", - "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/types": "5.7.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1072,13 +1075,15 @@ } }, "node_modules/cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "dependencies": { - "colors": "1.0.3", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" } }, "node_modules/clone-response": { @@ -1514,13 +1519,13 @@ } }, "node_modules/eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -1531,7 +1536,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", + "espree": "^9.2.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1649,9 +1654,9 @@ } }, "node_modules/espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", "dev": true, "dependencies": { "acorn": "^8.6.0", @@ -1858,9 +1863,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", "funding": [ { "type": "individual", @@ -2607,9 +2612,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" @@ -2771,22 +2776,26 @@ } }, "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3172,9 +3181,9 @@ "dev": true }, "node_modules/minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "devOptional": true, "dependencies": { "yallist": "^4.0.0" @@ -3509,15 +3518,15 @@ } }, "node_modules/npm-check-updates": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.0.2.tgz", - "integrity": "sha512-VzMNuUXqRta1qpBkFiE0hKfpOGP2FbYwpBBTcJXoJWppOPBSi/paWFVhPVWf7PgRoDWegK+PDAWKhIvhyrBrIg==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.0.5.tgz", + "integrity": "sha512-ns1liBBogwjmOVZY/PYgeIoarItwdOSBxccJDZKKkxsMkXges/Bp5CAnQIvYwlsz6fByQJFvqXSOqwIUBY6gpQ==", "dev": true, "dependencies": { "chalk": "^4.1.2", "cint": "^8.2.1", - "cli-table": "^0.3.6", - "commander": "^6.2.1", + "cli-table": "^0.3.11", + "commander": "^8.3.0", "fast-memoize": "^2.5.2", "find-up": "5.0.0", "fp-and-or": "^0.1.3", @@ -3539,7 +3548,7 @@ "rimraf": "^3.0.2", "semver": "^7.3.5", "semver-utils": "^1.1.4", - "source-map-support": "^0.5.20", + "source-map-support": "^0.5.21", "spawn-please": "^1.0.0", "update-notifier": "^5.1.0" }, @@ -3552,12 +3561,12 @@ } }, "node_modules/npm-check-updates/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 12" } }, "node_modules/npm-install-checks": { @@ -3655,9 +3664,9 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4801,9 +4810,9 @@ } }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4932,9 +4941,9 @@ } }, "node_modules/usb": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.1.tgz", - "integrity": "sha512-T6DZbJAFNcxhY1FzaYdXhV2oqoRlvLhtSSmnbFAqyCxahUkc+g0BPZVXv7hIeQQxDCAQnr4Ia8bfOk1JlzNzzw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -5195,14 +5204,14 @@ } }, "@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", + "espree": "^9.2.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", @@ -5243,12 +5252,12 @@ "integrity": "sha512-axFX7lN2Yd7bz/6SlD7dzq5sY/N9+XYuLHPukuiyHQRDtNMRL1uDqJhOx6RVaN2tYDHB75h7YxRQWP7U44Mgzg==" }, "@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" } @@ -5309,9 +5318,9 @@ } }, "@npmcli/fs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", - "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", "dev": true, "requires": { "@gar/promisify": "^1.0.1", @@ -5449,19 +5458,19 @@ "dev": true }, "@types/node": { - "version": "16.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", - "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz", + "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", + "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.5.0", - "@typescript-eslint/scope-manager": "5.5.0", + "@typescript-eslint/experimental-utils": "5.7.0", + "@typescript-eslint/scope-manager": "5.7.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -5471,55 +5480,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz", - "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz", - "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", + "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz", - "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" } }, "@typescript-eslint/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz", - "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz", - "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -5528,12 +5537,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz", - "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/types": "5.7.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -5926,13 +5935,12 @@ "dev": true }, "cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "requires": { - "colors": "1.0.3", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" } }, "clone-response": { @@ -6273,13 +6281,13 @@ "dev": true }, "eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -6290,7 +6298,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", + "espree": "^9.2.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -6376,9 +6384,9 @@ "dev": true }, "espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", "dev": true, "requires": { "acorn": "^8.6.0", @@ -6546,9 +6554,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" }, "foreach": { "version": "2.0.5", @@ -7090,9 +7098,9 @@ "dev": true }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, "is-npm": { @@ -7194,19 +7202,23 @@ "dev": true }, "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" } }, "is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } }, "is-yarn-global": { "version": "0.3.0", @@ -7516,9 +7528,9 @@ "dev": true }, "minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "devOptional": true, "requires": { "yallist": "^4.0.0" @@ -7777,15 +7789,15 @@ } }, "npm-check-updates": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.0.2.tgz", - "integrity": "sha512-VzMNuUXqRta1qpBkFiE0hKfpOGP2FbYwpBBTcJXoJWppOPBSi/paWFVhPVWf7PgRoDWegK+PDAWKhIvhyrBrIg==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.0.5.tgz", + "integrity": "sha512-ns1liBBogwjmOVZY/PYgeIoarItwdOSBxccJDZKKkxsMkXges/Bp5CAnQIvYwlsz6fByQJFvqXSOqwIUBY6gpQ==", "dev": true, "requires": { "chalk": "^4.1.2", "cint": "^8.2.1", - "cli-table": "^0.3.6", - "commander": "^6.2.1", + "cli-table": "^0.3.11", + "commander": "^8.3.0", "fast-memoize": "^2.5.2", "find-up": "5.0.0", "fp-and-or": "^0.1.3", @@ -7807,15 +7819,15 @@ "rimraf": "^3.0.2", "semver": "^7.3.5", "semver-utils": "^1.1.4", - "source-map-support": "^0.5.20", + "source-map-support": "^0.5.21", "spawn-please": "^1.0.0", "update-notifier": "^5.1.0" }, "dependencies": { "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true } } @@ -7897,9 +7909,9 @@ } }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", "dev": true }, "object-is": { @@ -8719,9 +8731,9 @@ } }, "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true }, "typescript-axios-wb": { @@ -8822,9 +8834,9 @@ } }, "usb": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.1.tgz", - "integrity": "sha512-T6DZbJAFNcxhY1FzaYdXhV2oqoRlvLhtSSmnbFAqyCxahUkc+g0BPZVXv7hIeQQxDCAQnr4Ia8bfOk1JlzNzzw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", "optional": true, "requires": { "node-addon-api": "^4.2.0", diff --git a/package.json b/package.json index cca0fb4e..61f5cdbe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Homebridge SwitchBot", "name": "@switchbot/homebridge-switchbot", - "version": "1.3.0", + "version": "1.4.0", "description": "The [Homebridge](https://homebridge.io) SwitchBot plugin allows you to access your [SwitchBot](https://www.switch-bot.com) device(s) from HomeKit.", "author": "SwitchBot (https://github.com/SwitchBot)", "license": "ISC", @@ -14,7 +14,7 @@ }, "engines": { "homebridge": "^1.3.8", - "node": "^14.18.1 || ^16.13.0" + "node": "^14.18.2 || ^16.13.1" }, "main": "dist/index.js", "scripts": { @@ -53,16 +53,16 @@ "rxjs": "^7.4.0" }, "devDependencies": { - "@types/node": "^16.11.11", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "eslint": "^8.3.0", + "@types/node": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", + "eslint": "^8.4.1", "homebridge": "^1.3.8", "nodemon": "^2.0.15", - "npm-check-updates": "^12.0.2", + "npm-check-updates": "^12.0.5", "rimraf": "^3.0.2", "ts-node": "^10.4.0", - "typescript": "^4.5.2", + "typescript": "^4.5.4", "typescript-axios-wb": "^1.0.3" } } \ No newline at end of file diff --git a/src/devices/bots.ts b/src/devices/bots.ts index 1478c78d..2bdf517d 100644 --- a/src/devices/bots.ts +++ b/src/devices/bots.ts @@ -3,7 +3,7 @@ import { AxiosResponse } from 'axios'; import Switchbot from 'node-switchbot'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform'; -import { debounceTime, skipWhile, tap } from 'rxjs/operators'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, payload } from '../settings'; @@ -20,6 +20,7 @@ export class Bot { // Characteristic Values On!: CharacteristicValue; + OnCached!: CharacteristicValue; BatteryLevel!: CharacteristicValue; StatusLowBattery!: CharacteristicValue; @@ -48,11 +49,15 @@ export class Bot { public device: device & devicesConfig, ) { // Bot Config - this.platform.device(`Bot: ${this.accessory.displayName} Config: (ble: ${device.ble}, mode: ${device.bot?.mode},` + this.platform.device(`Bot: ${this.accessory.displayName} Config: (ble: ${device.ble}, offline: ${device.offline}, mode: ${device.bot?.mode},` + ` deviceType: ${device.bot?.deviceType})`); // default placeholders - this.On = false; + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } this.BatteryLevel = 100; this.StatusLowBattery = 1; @@ -61,7 +66,7 @@ export class Bot { this.botUpdateInProgress = false; // Retrieve initial values and updateHomekit - this.parseStatus(); + this.refreshStatus(); // set accessory information accessory @@ -122,7 +127,6 @@ export class Bot { } // Retrieve initial values and updateHomekit - this.refreshStatus(); this.updateHomeKitCharacteristics(); // Start an update interval @@ -197,6 +201,12 @@ export class Bot { this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI parseStatus`); if (this.device.bot?.mode === 'press') { this.On = false; + } else { + if (this.deviceStatus.body.power === 'on') { + this.On = true; + } else { + this.On = false; + } } this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.On}`); } @@ -284,7 +294,9 @@ export class Bot { if (this.platform.config.credentials?.openToken) { this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI refreshStatus`); try { - this.deviceStatus = { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + this.platform.device(`Bot: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + /*this.deviceStatus = { statusCode: 100, body: { deviceId: this.device.deviceId!, @@ -293,7 +305,7 @@ export class Bot { power: 'on', }, message: 'success', - }; + };*/ this.parseStatus(); this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -324,61 +336,71 @@ export class Bot { } else { await this.openAPIpushChanges(); } - this.refreshStatus(); + interval(5000) + .pipe(skipWhile(() => this.botUpdateInProgress)) + .pipe(take(1)) + .subscribe(() => { + this.refreshStatus(); + }); } private async BLEpushChanges() { - this.platform.debug(`Bot: ${this.accessory.displayName} BLE pushChanges`); - const switchbot = this.connectBLE(); - if (this.device.bot?.mode === 'press') { - this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); - switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => { - this.platform.log.info(`Bot: ${this.accessory.displayName}, On: ${this.On}`); - return device_list[0].press({ id: this.device.bleMac }); - }).then(() => { - this.platform.device(`Bot: ${this.accessory.displayName} Done.`); - }).catch(async (e: any) => { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); - if (this.deviceDebug) { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error Message: ${JSON.stringify(e.message)}`); - } - if (this.debugDebug) { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error: ${JSON.stringify(e)}`); - } - if (this.platform.config.credentials?.openToken) { - this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); - await this.openAPIpushChanges(); - } - this.apiError(e); - }); - } else if (this.device.bot?.mode === 'switch') { - this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); - switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list: any) => { - this.platform.log.info(`Bot: ${this.accessory.displayName} On: ${this.On}`); - return this.turnOnOff(device_list); - }).then(() => { - this.platform.device(`Bot: ${this.accessory.displayName} Done.`); - }).catch(async (e: any) => { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); - if (this.deviceDebug) { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error Message: ${JSON.stringify(e.message)}`); - } - if (this.debugDebug) { - this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error: ${JSON.stringify(e)}`); - } - if (this.platform.config.credentials?.openToken) { - this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); - await this.openAPIpushChanges(); - } - this.apiError(e); - }); - } else { - this.platform.log.error(`Bot: ${this.accessory.displayName} Mode Not Set, mode: ${this.device.bot?.mode}`); + if (this.On !== this.OnCached) { + this.platform.debug(`Bot: ${this.accessory.displayName} BLE pushChanges`); + const switchbot = this.connectBLE(); + if (this.device.bot?.mode === 'press') { + this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); + switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }) + .then((device_list: { press: (arg0: { id: string | undefined; }) => any; }[]) => { + this.platform.log.info(`Bot: ${this.accessory.displayName}, On: ${this.On}`); + return device_list[0].press({ id: this.device.bleMac }); + }).then(() => { + this.platform.device(`Bot: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } + this.apiError(e); + }); + } else if (this.device.bot?.mode === 'switch') { + this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); + switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list: any) => { + this.platform.log.info(`Bot: ${this.accessory.displayName} On: ${this.On}`); + return this.turnOnOff(device_list); + }).then(() => { + this.platform.device(`Bot: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } + this.apiError(e); + }); + } else { + this.platform.log.error(`Bot: ${this.accessory.displayName} Mode Not Set, mode: ${this.device.bot?.mode}`); + } } + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; } private turnOnOff(device_list: any) { @@ -392,35 +414,39 @@ export class Bot { private async openAPIpushChanges() { if (this.platform.config.credentials?.openToken) { try { - this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI pushChanges`); - const payload = { - commandType: 'command', - parameter: 'default', - } as payload; - - if (this.device.bot?.mode === 'switch' && this.On) { - payload.command = 'turnOn'; - this.On = true; - this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); - } else if (this.device.bot?.mode === 'switch' && !this.On) { - payload.command = 'turnOff'; - this.On = false; - this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); - } else if (this.device.bot?.mode === 'press') { - payload.command = 'press'; - this.platform.debug(`Bot: ${this.accessory.displayName} Press Mode`); - this.On = false; - } else { - throw new Error(`Bot: ${this.accessory.displayName} Device Paramters not set for this Bot.`); - } + if (this.On !== this.OnCached) { + this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI pushChanges`); + const payload = { + commandType: 'command', + parameter: 'default', + } as payload; + + if (this.device.bot?.mode === 'switch' && this.On) { + payload.command = 'turnOn'; + this.On = true; + this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); + } else if (this.device.bot?.mode === 'switch' && !this.On) { + payload.command = 'turnOff'; + this.On = false; + this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); + } else if (this.device.bot?.mode === 'press') { + payload.command = 'press'; + this.platform.debug(`Bot: ${this.accessory.displayName} Press Mode`); + this.On = false; + } else { + throw new Error(`Bot: ${this.accessory.displayName} Device Paramters not set for this Bot.`); + } - this.platform.log.info(`Bot: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` - + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + this.platform.log.info(`Bot: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Bot ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); - this.statusCode(push); + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Bot ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; + } } catch (e: any) { this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); if (this.deviceDebug) { @@ -491,9 +517,11 @@ export class Bot { break; case 161: this.platform.log.error(`Bot: ${this.accessory.displayName} Device is offline.`); + this.offlineOff(); break; case 171: - this.platform.log.error(`Bot: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Bot: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); + this.offlineOff(); break; case 190: this.platform.log.error(`Bot: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` @@ -507,6 +535,17 @@ export class Bot { } } + private offlineOff() { + if (this.device.offline) { + this.On = false; + if (this.device.bot?.deviceType === 'switch') { + this.switchService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } else { + this.outletService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } + } + } + /** * Handle requests to set the "On" characteristic */ diff --git a/src/devices/colorbulb.ts b/src/devices/colorbulb.ts index 4a466fc2..149eeab2 100644 --- a/src/devices/colorbulb.ts +++ b/src/devices/colorbulb.ts @@ -56,13 +56,17 @@ export class ColorBulb { this.platform.device(`Color Bulb: ${this.accessory.displayName} Config: (ble: ${device.ble}, set_minStep: ${device.colorbulb?.set_minStep}`); // default placeholders - this.minKelvin = 2000; - this.maxKelvin = 9000; + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } this.Hue = 0; - this.On = false; this.Brightness = 0; this.Saturation = 0; this.ColorTemperature = 140; + this.minKelvin = 2000; + this.maxKelvin = 9000; // this is subject we use to track when we need to POST changes to the SwitchBot API this.doColorBulbUpdate = new Subject(); @@ -299,6 +303,7 @@ export class ColorBulb { this.platform.debug(`Color Bulb: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); this.statusCode(push); this.OnCached = this.On; + this.accessory.context.On = this.OnCached; } // Push Brightness Update if (this.On) { @@ -498,9 +503,11 @@ export class ColorBulb { break; case 161: this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Device is offline.`); + this.offlineOff(); break; case 171: - this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} is offline. Hub: ${this.device.hubDeviceId}`); + this.offlineOff(); break; case 190: this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` @@ -514,6 +521,13 @@ export class ColorBulb { } } + private offlineOff() { + if (this.device.offline) { + this.On = false; + this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } + } + /** * Handle requests to set the value of the "On" characteristic */ diff --git a/src/devices/contact.ts b/src/devices/contact.ts index 0b8a3746..3c92bffb 100644 --- a/src/devices/contact.ts +++ b/src/devices/contact.ts @@ -50,7 +50,7 @@ export class Contact { public device: device & devicesConfig, ) { // Contact Config - this.platform.device(`Contact Sensor: ${this.accessory.displayName} Config: (ble: ${device.ble},` + this.platform.device(`Contact Sensor: ${this.accessory.displayName} Config: (ble: ${device.ble}, offline: ${device.offline},` + ` hide_lightsensor: ${device.contact?.hide_lightsensor}, hide_motionsensor: ${device.contact?.hide_motionsensor})`); diff --git a/src/devices/curtains.ts b/src/devices/curtains.ts index d7f92c3f..610af093 100644 --- a/src/devices/curtains.ts +++ b/src/devices/curtains.ts @@ -3,7 +3,7 @@ import { AxiosResponse } from 'axios'; import Switchbot from 'node-switchbot'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform'; -import { debounceTime, skipWhile, tap } from 'rxjs/operators'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload } from '../settings'; @@ -487,7 +487,12 @@ export class Curtain { } else { await this.OpenAPIpushChanges(); } - this.refreshStatus(); + interval(5000) + .pipe(skipWhile(() => this.curtainUpdateInProgress)) + .pipe(take(1)) + .subscribe(() => { + this.refreshStatus(); + }); } private async BLEpushChanges() { @@ -632,7 +637,7 @@ export class Curtain { this.platform.log.error(`Curtain: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Curtain: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Curtain: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` diff --git a/src/devices/humidifiers.ts b/src/devices/humidifiers.ts index 62c53359..41929772 100644 --- a/src/devices/humidifiers.ts +++ b/src/devices/humidifiers.ts @@ -2,7 +2,7 @@ import { AxiosResponse } from 'axios'; import Switchbot from 'node-switchbot'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform'; -import { debounceTime, skipWhile, tap } from 'rxjs/operators'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { DeviceURL, device, devicesConfig, serviceData, ad, deviceStatusResponse, payload } from '../settings'; @@ -50,7 +50,7 @@ export class Humidifier { public device: device & devicesConfig, ) { // Humidifiers Config - this.platform.device(`Humidifier: ${this.accessory.displayName} Config: (ble: ${device.ble}, hide_temperature: ` + this.platform.device(`Humidifier: ${this.accessory.displayName} Config: (ble: ${device.ble}, offline: ${device.offline}, hide_temperature: ` + `${device.humidifier?.hide_temperature}, set_minStep: ${device.humidifier?.set_minStep})`); // default placeholders @@ -394,7 +394,12 @@ export class Humidifier { //} else { await this.OpenAPIpushChanges(); //} - this.refreshStatus(); + interval(5000) + .pipe(skipWhile(() => this.humidifierUpdateInProgress)) + .pipe(take(1)) + .subscribe(() => { + this.refreshStatus(); + }); } private async BLEpushChanges() { @@ -628,9 +633,11 @@ export class Humidifier { break; case 161: this.platform.log.error(`Humidifier: ${this.accessory.displayName} Device is offline.`); + this.offlineOff(); break; case 171: - this.platform.log.error(`Humidifier: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); + this.offlineOff(); break; case 190: this.platform.log.error(`Humidifier: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` @@ -644,6 +651,13 @@ export class Humidifier { } } + private offlineOff() { + if (this.device.offline) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + this.service.getCharacteristic(this.platform.Characteristic.Active).updateValue(this.Active); + } + } + /** * Handle requests to set the "Target Humidifier Dehumidifier State" characteristic */ diff --git a/src/devices/indoorcam.ts b/src/devices/indoorcam.ts index 0470dcc9..110e2ae3 100644 --- a/src/devices/indoorcam.ts +++ b/src/devices/indoorcam.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform'; -import { debounceTime, skipWhile, tap } from 'rxjs/operators'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload } from '../settings'; @@ -10,8 +10,13 @@ export class IndoorCam { private service: Service; // Characteristic Values - On!: CharacteristicValue; - OutletInUse!: CharacteristicValue; + Active!: CharacteristicValue; + SetupEndpoints!: CharacteristicValue; + StreamingStatus!: CharacteristicValue; + SupportedRTPConfiguration!: CharacteristicValue; + SelectedRTPStreamConfiguration!: CharacteristicValue; + SupportedVideoStreamConfiguration!: CharacteristicValue; + SupportedAudioStreamConfiguration!: CharacteristicValue; // OpenAPI Others deviceStatus!: deviceStatusResponse; @@ -29,9 +34,12 @@ export class IndoorCam { private accessory: PlatformAccessory, public device: device & devicesConfig, ) { + // Indoor Cam Config + this.platform.device(`Indoor Cam: ${this.accessory.displayName} Config: (hide_device: ${device.hide_device})`); + + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} was added but will not work as OpenAPI doesn't support it yet.`); // default placeholders - this.On = false; - this.OutletInUse = true; + this.SelectedRTPStreamConfiguration; // this is subject we use to track when we need to POST changes to the SwitchBot API this.doCameraUpdate = new Subject(); @@ -50,8 +58,8 @@ export class IndoorCam { // get the WindowCovering service if it exists, otherwise create a new WindowCovering service // you can create multiple services for each accessory (this.service = - accessory.getService(this.platform.Service.Outlet) || - accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Indoor Camera`; + accessory.getService(this.platform.Service.CameraRTPStreamManagement) || + accessory.addService(this.platform.Service.CameraRTPStreamManagement)), `${accessory.displayName} Indoor Camera`; // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error, // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id: @@ -65,9 +73,26 @@ export class IndoorCam { // see https://developers.homebridge.io/#/service/WindowCovering // create handlers for required characteristics - this.service.getCharacteristic(this.platform.Characteristic.On).onSet(this.OnSet.bind(this)); + this.service.getCharacteristic(this.platform.Characteristic.Active) + .onSet(this.ActiveSet.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration) + .onSet(this.SelectedRTPStreamConfigurationSet.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.SetupEndpoints) + .onSet(this.SetupEndpointsSet.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.StreamingStatus) + .onSet(this.StreamingStatusSet.bind(this)); - this.service.setCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse || true); + this.service.getCharacteristic(this.platform.Characteristic.SupportedAudioStreamConfiguration) + .onSet(this.SupportedAudioStreamConfigurationSet.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.SupportedRTPConfiguration) + .onSet(this.SupportedRTPConfigurationSet.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.SupportedVideoStreamConfiguration) + .onSet(this.SupportedVideoStreamConfigurationSet.bind(this)); // Update Homekit this.updateHomeKitCharacteristics(); @@ -108,14 +133,7 @@ export class IndoorCam { } parseStatus() { - switch (this.deviceStatus.body.power) { - case 'on': - this.On = true; - break; - default: - this.On = false; - } - this.platform.debug(`Indoor Cam ${this.accessory.displayName} On: ${this.On}`); + this.platform.debug(`Indoor Cam ${this.accessory.displayName} SelectedRTPStreamConfiguration: ${this.SelectedRTPStreamConfiguration}`); } @@ -146,45 +164,42 @@ export class IndoorCam { * Camera - "command" "turnOn" "default" = set to ON state */ async pushChanges() { - const payload = { - commandType: 'command', - parameter: 'default', - } as payload; - - if (this.On) { - payload.command = 'turnOn'; + if (!this.Active) { + const payload = { + commandType: 'command', + parameter: 'default', + command: 'turnOn', + } as payload; + + this.platform.log.info(`Indoor Cam: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + interval(5000) + .pipe(skipWhile(() => this.cameraUpdateInProgress)) + .pipe(take(1)) + .subscribe(() => { + this.refreshStatus(); + }); } else { - payload.command = 'turnOff'; + this.platform.device(`Indoor Cam: ${this.accessory.displayName} No pushChanges. Active: ${this.Active}`); } - - this.platform.log.info(`Indoor Cam: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` - + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); - - // Make the API request - const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`Indoor Cam: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); - this.statusCode(push); - this.refreshStatus(); } updateHomeKitCharacteristics() { - if (this.On === undefined) { - this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${this.On}`); - } else { - this.service.updateCharacteristic(this.platform.Characteristic.On, this.On); - this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); - } - if (this.OutletInUse === undefined) { - this.platform.debug(`Indoor Cam: ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`); + if (this.SelectedRTPStreamConfiguration === undefined) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${this.SelectedRTPStreamConfiguration}`); } else { - this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse); - this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`); + this.service.updateCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration, this.SelectedRTPStreamConfiguration); + this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic On: ${this.SelectedRTPStreamConfiguration}`); } } public apiError(e: any) { - this.service.updateCharacteristic(this.platform.Characteristic.On, e); - this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e); + this.service.updateCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration, e); } private statusCode(push: AxiosResponse<{ statusCode: number; }>) { @@ -200,9 +215,15 @@ export class IndoorCam { break; case 161: this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device is offline.`); + if (this.device.offline) { + this.Active = false; + } break; case 171: - this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); + if (this.device.offline) { + this.Active = false; + } break; case 190: this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` @@ -216,15 +237,73 @@ export class IndoorCam { } } + /** + * Handle requests to set the value of the "Active" characteristic + */ + ActiveSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} Active: ${value}`); + + this.Active = value; + this.doCameraUpdate.next(); + } + + /** + * Handle requests to set the value of the "SelectedRTPStreamConfiguration" characteristic + */ + SelectedRTPStreamConfigurationSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SelectedRTPStreamConfiguration: ${value}`); + + this.SelectedRTPStreamConfiguration = value; + this.doCameraUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + SetupEndpointsSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SetupEndpoints: ${value}`); + + this.SetupEndpoints = value; + this.doCameraUpdate.next(); + } + /** * Handle requests to set the value of the "On" characteristic */ - OnSet(value: CharacteristicValue) { - this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${value}`); + SupportedAudioStreamConfigurationSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedAudioStreamConfiguration: ${value}`); - this.On = value; + this.SupportedAudioStreamConfiguration = value; this.doCameraUpdate.next(); } + /** + * Handle requests to set the value of the "On" characteristic + */ + SupportedRTPConfigurationSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedRTPConfiguration: ${value}`); + + this.SupportedRTPConfiguration = value; + this.doCameraUpdate.next(); + } + /** + * Handle requests to set the value of the "On" characteristic + */ + StreamingStatusSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} StreamingStatus: ${value}`); + + this.StreamingStatus = value; + this.doCameraUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + SupportedVideoStreamConfigurationSet(value: CharacteristicValue) { + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedVideoStreamConfiguration: ${value}`); + + this.SupportedVideoStreamConfiguration = value; + this.doCameraUpdate.next(); + } } \ No newline at end of file diff --git a/src/devices/meters.ts b/src/devices/meters.ts index 8e1462f7..2b075e7e 100644 --- a/src/devices/meters.ts +++ b/src/devices/meters.ts @@ -3,7 +3,7 @@ import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { SwitchBotPlatform } from '../platform'; import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge'; -import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature } from '../settings'; /** * Platform Accessory @@ -18,8 +18,11 @@ export class Meter { // Characteristic Values CurrentRelativeHumidity!: CharacteristicValue; + CurrentRelativeHumidityCached!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; + CurrentTemperatureCached!: CharacteristicValue; BatteryLevel!: CharacteristicValue; + BatteryLevelCached!: CharacteristicValue; ChargingState!: CharacteristicValue; StatusLowBattery!: CharacteristicValue; Active!: CharacteristicValue; @@ -32,10 +35,9 @@ export class Meter { connected?: boolean; switchbot!: switchbot; serviceData!: serviceData; + temperature!: temperature['c']; battery!: serviceData['battery']; humidity!: serviceData['humidity']; - fahrenheit!: serviceData['fahrenheit']; - temperature!: serviceData['temperature']; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -51,15 +53,31 @@ export class Meter { public device: device & devicesConfig, ) { // Meter Config - this.platform.device(`Meter: ${this.accessory.displayName} Config: (ble: ${device.ble}, unit: ${device.meter?.unit},` - + ` hide_temperature: ${device.meter?.hide_temperature}, hide_humidity: ${device.meter?.hide_humidity})`); + this.platform.device(`Meter: ${this.accessory.displayName} Config: (ble: ${device.ble}, hide_temperature: ${device.meter?.hide_temperature},` + + ` hide_humidity: ${device.meter?.hide_humidity})`); // default placeholders - this.BatteryLevel = 0; - this.ChargingState = 2; - this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; - this.CurrentRelativeHumidity = 0; - this.CurrentTemperature = 0; + if (this.BatteryLevel === undefined) { + this.BatteryLevel = 100; + } else { + this.BatteryLevel = this.accessory.context.BatteryLevel; + } + if (this.BatteryLevel < 15) { + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + } else { + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + } + this.ChargingState = this.platform.Characteristic.ChargingState.NOT_CHARGEABLE; + if (this.CurrentRelativeHumidity === undefined) { + this.CurrentRelativeHumidity = 0; + } else { + this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity; + } + if (this.CurrentTemperature === undefined) { + this.CurrentTemperature = 0; + } else { + this.CurrentTemperature = this.accessory.context.CurrentTemperature; + } // this is subject we use to track when we need to POST changes to the SwitchBot API this.doMeterUpdate = new Subject(); @@ -177,9 +195,9 @@ export class Meter { // Battery this.BatteryLevel = Number(this.battery); if (this.BatteryLevel < 15) { - this.StatusLowBattery = 1; + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = 0; + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.platform.debug(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); @@ -191,13 +209,8 @@ export class Meter { // Current Temperature if (!this.device.meter?.hide_temperature) { - if (this.device.meter?.unit === 1) { - this.CurrentTemperature = this.toFahrenheit(this.temperature!.c); - } else if (this.device.meter?.unit === 0) { - this.CurrentTemperature = this.toCelsius(this.temperature!.c); - } else { - this.CurrentTemperature = Number(this.temperature?.c); - } + this.temperature < 0 ? 0 : this.temperature > 100 ? 100 : this.temperature; + this.CurrentTemperature = this.temperature; this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); } } @@ -211,9 +224,9 @@ export class Meter { this.BatteryLevel = 10; } if (this.BatteryLevel < 15) { - this.StatusLowBattery = 1; + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = 0; + this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } // Current Relative Humidity if (!this.device.meter?.hide_humidity) { @@ -223,13 +236,7 @@ export class Meter { // Current Temperature if (!this.device.meter?.hide_temperature) { - if (this.device.meter?.unit === 1) { - this.CurrentTemperature = this.toFahrenheit(this.deviceStatus.body.temperature!); - } else if (this.device.meter?.unit === 0) { - this.CurrentTemperature = this.toCelsius(this.deviceStatus.body.temperature!); - } else { - this.CurrentTemperature = Number(this.deviceStatus.body.temperature); - } + this.CurrentTemperature = Number(this.deviceStatus.body.temperature); this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); } } @@ -259,20 +266,18 @@ export class Meter { const switchbot = this.connectBLE(); // Start to monitor advertisement packets switchbot.startScan({ - model: 'T', id: this.device.bleMac, }).then(() => { // Set an event hander switchbot.onadvertisement = (ad: ad) => { this.serviceData = ad.serviceData; - this.temperature = ad.serviceData.temperature; - this.fahrenheit = ad.serviceData.fahrenheit; + this.temperature = ad.serviceData.temperature!.c; this.humidity = ad.serviceData.humidity; this.battery = ad.serviceData.battery; this.platform.device(`Meter: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); this.platform.device(`Meter: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` - + `temperature: ${JSON.stringify(ad.serviceData.temperature)}, fahrenheit: ${ad.serviceData.fahrenheit}, ` - + `humidity: ${ad.serviceData.humidity}, battery: ${ad.serviceData.battery}`); + + `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + + `battery: ${ad.serviceData.battery}`); if (this.serviceData) { this.connected = true; @@ -288,6 +293,12 @@ export class Meter { // Stop to monitor switchbot.stopScan(); if (this.connected) { + this.CurrentTemperatureCached = this.temperature; + this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached; + this.CurrentRelativeHumidityCached = this.humidity!; + this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidityCached; + this.BatteryLevelCached = this.battery!; + this.accessory.context.BatteryLevel = this.BatteryLevelCached; this.parseStatus(); this.updateHomeKitCharacteristics(); } else { @@ -321,6 +332,12 @@ export class Meter { try { this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; this.platform.debug(`Meter: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.CurrentTemperatureCached = this.deviceStatus.body.humidity!; + this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached; + this.CurrentRelativeHumidityCached = this.deviceStatus.body.humidity!; + this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidityCached; + this.BatteryLevelCached = 100; + this.accessory.context.BatteryLevel = this.BatteryLevelCached; this.parseStatus(); this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -382,19 +399,4 @@ export class Meter { this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e); } } - - /** - * Converts the value to celsius if the temperature units are in Fahrenheit - */ - toCelsius(value: number) { - // celsius should be to the nearest 0.5 degree - return Math.round((5 / 9) * (value - 32) * 2) / 2; - } - - /** - * Converts the value to fahrenheit if the temperature units are in Fahrenheit - */ - toFahrenheit(value: number) { - return Math.round((value * 9) / 5 + 32); - } } diff --git a/src/devices/plugs.ts b/src/devices/plugs.ts index 316018e2..dde823bd 100644 --- a/src/devices/plugs.ts +++ b/src/devices/plugs.ts @@ -2,7 +2,7 @@ import { AxiosResponse } from 'axios'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform'; -import { debounceTime, skipWhile, tap } from 'rxjs/operators'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload } from '../settings'; @@ -12,6 +12,7 @@ export class Plug { // Characteristic Values On!: CharacteristicValue; + OnCached!: CharacteristicValue; // OpenAPI Others deviceStatus!: deviceStatusResponse; @@ -29,8 +30,15 @@ export class Plug { private accessory: PlatformAccessory, public device: device & devicesConfig, ) { + // Bot Config + this.platform.device(`Plug: ${this.accessory.displayName} Config: (offline: ${device.offline})`); + // default placeholders - this.On = false; + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } // this is subject we use to track when we need to POST changes to the SwitchBot API this.doPlugUpdate = new Subject(); @@ -142,24 +150,34 @@ export class Plug { * Plug - "command" "turnOn" "default" = set to ON state */ async pushChanges() { - const payload = { - commandType: 'command', - parameter: 'default', - } as payload; - - if (this.On) { - payload.command = 'turnOn'; - } else { - payload.command = 'turnOff'; - } + if (this.On !== this.OnCached) { + const payload = { + commandType: 'command', + parameter: 'default', + } as payload; + + if (this.On) { + payload.command = 'turnOn'; + } else { + payload.command = 'turnOff'; + } - this.platform.log.info(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` - + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + this.platform.log.info(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); - this.statusCode(push); + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; + } + interval(5000) + .pipe(skipWhile(() => this.plugUpdateInProgress)) + .pipe(take(1)) + .subscribe(() => { + this.refreshStatus(); + }); } updateHomeKitCharacteristics() { @@ -188,9 +206,11 @@ export class Plug { break; case 161: this.platform.log.error(`Plug: ${this.accessory.displayName} Device is offline.`); + this.offlineOff(); break; case 171: - this.platform.log.error(`Plug: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Plug: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); + this.offlineOff(); break; case 190: this.platform.log.error(`Plug: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` @@ -204,6 +224,13 @@ export class Plug { } } + private offlineOff() { + if (this.device.offline) { + this.On = false; + this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } + } + /** * Handle requests to set the value of the "On" characteristic */ diff --git a/src/irdevices/airconditioners.ts b/src/irdevices/airconditioners.ts index c5a8bcea..495eb21e 100644 --- a/src/irdevices/airconditioners.ts +++ b/src/irdevices/airconditioners.ts @@ -14,9 +14,11 @@ export class AirConditioner { // Characteristic Values Active!: CharacteristicValue; + ActiveCached!: CharacteristicValue; RotationSpeed!: CharacteristicValue; LastTemperature!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; + CurrentTemperatureCached!: CharacteristicValue; TargetHeaterCoolerState?: CharacteristicValue; CurrentHeaterCoolerState!: CharacteristicValue; HeatingThresholdTemperature?: CharacteristicValue; @@ -41,7 +43,17 @@ export class AirConditioner { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { - this.CurrentTemperature = 24; + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + if (this.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.accessory.context.CurrentTemperature; + } // set accessory information accessory @@ -161,6 +173,8 @@ export class AirConditioner { this.platform.device(`Air Conditioner: ${this.accessory.displayName} pushAirConditionerOffChanges, Active: ${this.Active}`); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } private updateHomeKitCharacteristics() { @@ -349,6 +363,8 @@ export class AirConditioner { const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); this.statusCode(push); + this.CurrentTemperatureCached = this.CurrentTemperature; + this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached; this.updateHomeKitCharacteristics(); } catch (e: any) { this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} failed pushChanges`); @@ -379,7 +395,7 @@ export class AirConditioner { this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/airpurifiers.ts b/src/irdevices/airpurifiers.ts index f8de4877..aa473b77 100644 --- a/src/irdevices/airpurifiers.ts +++ b/src/irdevices/airpurifiers.ts @@ -15,11 +15,13 @@ export class AirPurifier { // Characteristic Values Active!: CharacteristicValue; APActive!: CharacteristicValue; + ActiveCached!: CharacteristicValue; CurrentAPTemp!: CharacteristicValue; CurrentAPMode!: CharacteristicValue; RotationSpeed!: CharacteristicValue; CurrentAPFanSpeed!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; + CurrentTemperatureCached!: CharacteristicValue; CurrentAirPurifierState!: CharacteristicValue; CurrentHeaterCoolerState!: CharacteristicValue; @@ -42,6 +44,18 @@ export class AirPurifier { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + if (this.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.accessory.context.CurrentTemperature; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -81,6 +95,8 @@ export class AirPurifier { this.pushAirConditionerOnChanges(); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } private updateHomeKitCharacteristics() { @@ -210,6 +226,8 @@ export class AirPurifier { this.platform.debug(`Air Purifier: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); this.updateHomeKitCharacteristics(); + this.CurrentTemperatureCached = this.CurrentTemperature; + this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached; } catch (e: any) { this.platform.log.error(`Air Purifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); if (this.deviceDebug) { @@ -239,7 +257,7 @@ export class AirPurifier { this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/cameras.ts b/src/irdevices/cameras.ts index 31d45afa..8a978c01 100644 --- a/src/irdevices/cameras.ts +++ b/src/irdevices/cameras.ts @@ -14,6 +14,7 @@ export class Camera { // Characteristic Values On!: CharacteristicValue; + OnCached!: CharacteristicValue; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -24,6 +25,13 @@ export class Camera { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -110,6 +118,8 @@ export class Camera { this.platform.debug(`Camera: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); this.updateHomeKitCharacteristics(); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; } catch (e: any) { this.platform.log.error(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); if (this.deviceDebug) { @@ -139,7 +149,7 @@ export class Camera { this.platform.log.error(`Camera: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Camera: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Camera: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Camera: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/fans.ts b/src/irdevices/fans.ts index 9d426953..e4127a57 100644 --- a/src/irdevices/fans.ts +++ b/src/irdevices/fans.ts @@ -14,6 +14,7 @@ export class Fan { // Characteristic Values Active!: CharacteristicValue; + ActiveCached!: CharacteristicValue; ActiveIdentifier!: CharacteristicValue; RotationSpeed!: CharacteristicValue; SwingMode!: CharacteristicValue; @@ -34,6 +35,13 @@ export class Fan { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -164,6 +172,8 @@ export class Fan { this.pushFanOnChanges(); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } /** @@ -261,7 +271,7 @@ export class Fan { this.platform.log.error(`Fan: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Fan: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Fan: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Fan: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/lights.ts b/src/irdevices/lights.ts index 77069135..92cbedf4 100644 --- a/src/irdevices/lights.ts +++ b/src/irdevices/lights.ts @@ -14,6 +14,7 @@ export class Light { // Characteristic Values On!: CharacteristicValue; + OnCached!: CharacteristicValue; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -24,6 +25,13 @@ export class Light { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -142,6 +150,8 @@ export class Light { const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); this.platform.debug(`Light: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; this.updateHomeKitCharacteristics(); } catch (e: any) { this.platform.log.error(`Light: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); @@ -173,7 +183,7 @@ export class Light { this.platform.log.error(`Light: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Light: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Light: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Light: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/others.ts b/src/irdevices/others.ts index 2dd51f04..8c792258 100644 --- a/src/irdevices/others.ts +++ b/src/irdevices/others.ts @@ -14,6 +14,7 @@ export class Others { // Characteristic Values Active!: CharacteristicValue; + ActiveCached!: CharacteristicValue; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -24,6 +25,13 @@ export class Others { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -54,6 +62,8 @@ export class Others { this.pushOffChanges(); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } private updateHomeKitCharacteristics() { @@ -160,7 +170,7 @@ export class Others { this.platform.log.error(`Other: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Other: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Other: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Other: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/tvs.ts b/src/irdevices/tvs.ts index ef5756ea..783fb97e 100644 --- a/src/irdevices/tvs.ts +++ b/src/irdevices/tvs.ts @@ -15,6 +15,7 @@ export class TV { // Characteristic Values Active!: CharacteristicValue; + ActiveCached!: CharacteristicValue; ActiveIdentifier!: CharacteristicValue; // Others @@ -29,6 +30,13 @@ export class TV { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -207,6 +215,8 @@ export class TV { this.pushTvOnChanges(); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } } @@ -377,7 +387,7 @@ export class TV { this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`${this.device.remoteType}: ` diff --git a/src/irdevices/vacuumcleaners.ts b/src/irdevices/vacuumcleaners.ts index b2977ed5..33e25900 100644 --- a/src/irdevices/vacuumcleaners.ts +++ b/src/irdevices/vacuumcleaners.ts @@ -14,6 +14,7 @@ export class VacuumCleaner { // Characteristic Values On!: CharacteristicValue; + OnCached!: CharacteristicValue; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -24,6 +25,13 @@ export class VacuumCleaner { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.On === undefined) { + this.On = false; + } else { + this.On = this.accessory.context.On; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -109,6 +117,8 @@ export class VacuumCleaner { const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; this.updateHomeKitCharacteristics(); } catch (e: any) { this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); @@ -139,7 +149,7 @@ export class VacuumCleaner { this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/irdevices/waterheaters.ts b/src/irdevices/waterheaters.ts index 7f435554..0e8ccba8 100644 --- a/src/irdevices/waterheaters.ts +++ b/src/irdevices/waterheaters.ts @@ -14,6 +14,7 @@ export class WaterHeater { // Characteristic Values Active!: CharacteristicValue; + ActiveCached!: CharacteristicValue; // Config private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; @@ -24,6 +25,13 @@ export class WaterHeater { private accessory: PlatformAccessory, public device: irdevice & irDevicesConfig, ) { + // default placeholders + if (this.Active === undefined) { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + // set accessory information accessory .getService(this.platform.Service.AccessoryInformation)! @@ -71,6 +79,8 @@ export class WaterHeater { this.service.setCharacteristic(this.platform.Characteristic.InUse, this.platform.Characteristic.InUse.IN_USE); } this.Active = value; + this.ActiveCached = this.Active; + this.accessory.context.Active = this.ActiveCached; } private updateHomeKitCharacteristics() { @@ -153,7 +163,7 @@ export class WaterHeater { this.platform.log.error(`Water Heater: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error(`Water Heater: ${this.accessory.displayName} Hub Device is offline.`); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`); break; case 190: this.platform.log.error(`Water Heater: ${this.accessory.displayName} Device internal error due to device states not synchronized` diff --git a/src/platform.ts b/src/platform.ts index c5bb9042..4a23f234 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -204,7 +204,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { const mergeBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => a1.map((itm: { deviceId: string; }) => ({ - ...a2.find((item: { deviceId: string; }) => (item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId) && item), + ...a2.find((item: { deviceId: string; }) => ( + item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '')) && item), ...itm, })); @@ -238,7 +239,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { const mergeIRBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => a1.map((itm: { deviceId: string; }) => ({ - ...a2.find((item: { deviceId: string; }) => (item.deviceId === itm.deviceId) && item), + ...a2.find((item: { deviceId: string; }) => ( + item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '')) && item), ...itm, })); @@ -265,8 +267,43 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.log.error('Neither SwitchBot OpenToken or Device Config are not set.'); } } catch (e: any) { - this.log.error('Failed to Discover Devices.', JSON.stringify(e.message)); - this.debug(JSON.stringify(e)); + if (e.message.includes('400')) { + this.log.error('Failed to Discover Devices: Bad Request'); + this.debug('The client has issued an invalid request. This is commonly used to specify validation errors in a request payload.'); + } else if (e.message.includes('401')) { + this.log.error('Failed to Discover Devices: Unauthorized Request'); + this.debug('Authorization for the API is required, but the request has not been authenticated.'); + } else if (e.message.includes('403')) { + this.log.error('Failed to Discover Devices: Forbidden Request'); + this.debug('The request has been authenticated but does not have appropriate permissions, or a requested resource is not found.'); + } else if (e.message.includes('404')) { + this.log.error('Failed to Discover Devices: Requst Not Found'); + this.debug('Specifies the requested path does not exist.'); + } else if (e.message.includes('406')) { + this.log.error('Failed to Discover Devices: Request Not Acceptable'); + this.debug('The client has requested a MIME type via the Accept header for a value not supported by the server.'); + } else if (e.message.includes('415')) { + this.log.error('Failed to Discover Devices: Unsupported Requst Header'); + this.debug('The client has defined a contentType header that is not supported by the server.'); + } else if (e.message.includes('422')) { + this.log.error('Failed to Discover Devices: Unprocessable Entity'); + this.debug('The client has made a valid request, but the server cannot process it.' + + ' This is often used for APIs for which certain limits have been exceeded.'); + } else if (e.message.includes('429')) { + this.log.error('Failed to Discover Devices: Too Many Requests'); + this.debug('The client has exceeded the number of requests allowed for a given time window.'); + } else if (e.message.includes('500')) { + this.log.error('Failed to Discover Devices: Internal Server Error'); + this.debug('An unexpected error on the SmartThings servers has occurred. These errors should be rare.'); + } else { + this.log.error('Failed to Discover Devices'); + } + if (this.config.options?.debug === 'device') { + this.log.error(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}`); + } + if (this.config.options?.debug === 'debug' || this.debugMode) { + this.log.error(`Failed to Discover Devices, Error: ${JSON.stringify(e)}`); + } } } @@ -310,7 +347,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debug(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createColorBulb(device); break; - case 'IndoorCam': + case 'Indoor Cam': this.debug(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createIndoorCam(device); break; @@ -420,6 +457,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -442,6 +480,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -475,6 +514,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -499,6 +539,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // accessory.context.firmwareRevision = findaccessories.accessoryAttribute.softwareRevision; // create the accessory handler for the newly create accessory @@ -531,6 +572,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -553,6 +595,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -584,6 +627,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -606,6 +650,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -637,6 +682,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -659,6 +705,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -690,6 +737,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -722,6 +770,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -766,6 +815,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -788,6 +838,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -819,6 +870,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -841,6 +893,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -872,6 +925,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeExistingAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory @@ -894,6 +948,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; await this.connectionTypeNewAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` @@ -923,7 +978,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -942,7 +998,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new TV(this, accessory, device); @@ -978,7 +1035,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1000,7 +1058,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new Fan(this, accessory, device); @@ -1031,7 +1090,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1053,7 +1113,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new Light(this, accessory, device); @@ -1084,7 +1145,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1106,7 +1168,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new AirConditioner(this, accessory, device); @@ -1137,7 +1200,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1159,7 +1223,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new AirPurifier(this, accessory, device); @@ -1190,7 +1255,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1212,7 +1278,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new WaterHeater(this, accessory, device); @@ -1243,7 +1310,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1265,7 +1333,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new VacuumCleaner(this, accessory, device); @@ -1296,7 +1365,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1318,7 +1388,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new Camera(this, accessory, device); @@ -1349,7 +1420,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.deviceName; existingAccessory.context.firmwareRevision = this.version; - await this.connectionTypeExistingAccessory(device, existingAccessory); + existingAccessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeExistingIRAccessory(device, existingAccessory); this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` @@ -1371,7 +1443,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; accessory.context.firmwareRevision = this.version; - await this.connectionTypeNewAccessory(device, accessory); + accessory.context.deviceType = `IR: ${device.remoteType}`; + await this.connectionTypeNewIRAccessory(device, accessory); // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new Others(this, accessory, device); @@ -1406,6 +1479,19 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } + public async connectionTypeNewIRAccessory( + device: irdevice & devicesConfig, + accessory: PlatformAccessory) { + accessory.context.connectionType = 'IR wirth OpenAPI'; + } + + public async connectionTypeExistingIRAccessory( + device: irdevice & devicesConfig, + existingAccessory: PlatformAccessory, + ) { + existingAccessory.context.connectionType = 'IR with OpenAPI'; + } + public unregisterPlatformAccessories(existingAccessory: PlatformAccessory) { // remove platform accessories when no longer present this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); diff --git a/src/settings.ts b/src/settings.ts index dd2eaeef..db0874e2 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -49,10 +49,10 @@ export interface devicesConfig extends device { colorbulb?: colorbulb; ble?: string; hide_device?: boolean; + offline?: boolean; } export type meter = { - unit?: number; hide_temperature?: boolean; hide_humidity?: boolean; }; @@ -280,10 +280,7 @@ export type serviceData = { //Humidifier's AutoMode autoMode?: boolean; //Meter Temperature Levels - temperature?: { - c: number, - f: number - }; + temperature?: temperature; // Fahrenheit enabled for Meter fahrenheit: boolean; // Humidity level for Meter @@ -303,6 +300,11 @@ export type serviceData = { position?: number; }; +export type temperature = { + c: number; + f: number; +}; + export type switchbot = { discover: ( arg0: