From 57efa4ad1ded2baa18033b934bee1256fd7d2c12 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 13 Feb 2024 06:54:58 -0600 Subject: [PATCH 01/73] v3.5.0 ## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) ### What's Changed - Add BLE support for `Smart Lock` - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 --- .vscode/settings.json | 3 +- CHANGELOG.md | 8 +++ config.schema.json | 4 +- package-lock.json | 163 +++++++++++++++++++++++------------------- package.json | 8 +-- 5 files changed, 105 insertions(+), 81 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 57325d61..d1d60974 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,5 +30,6 @@ "[jsonc]": { "editor.defaultFormatter": "vscode.json-language-features" }, - "codeQL.githubDatabase.update": "never" + "codeQL.githubDatabase.update": "never", + "codeQL.githubDatabase.download": "never" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8872aca2..ede68162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) +## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) + +### What's Changed +- Add BLE support for `Smart Lock` +- Housekeeping and updated dependencies. + +**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 + ## [3.4.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.4.0) (2024-02-11) ### What's Changed diff --git a/config.schema.json b/config.schema.json index e3643ad8..0040962e 100644 --- a/config.schema.json +++ b/config.schema.json @@ -821,14 +821,14 @@ "title": "Hide Lock's Contact Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' && model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" } }, "activate_latchbutton": { "title": "Activate Latch Button", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' && model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" } } } diff --git a/package-lock.json b/package-lock.json index 376c65dc..5bc96e44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@switchbot/homebridge-switchbot", - "version": "3.4.0", + "version": "3.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@switchbot/homebridge-switchbot", - "version": "3.4.0", + "version": "3.5.0", "funding": [ { "type": "Paypal", @@ -28,8 +28,8 @@ }, "devDependencies": { "@types/node": "^20.11.17", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", "eslint": "^8.56.0", "homebridge": "^1.7.0", "homebridge-config-ui-x": "4.55.1", @@ -44,7 +44,7 @@ "node": "^18 || ^20" }, "optionalDependencies": { - "node-switchbot": "2.0.3" + "node-switchbot": "2.1.0-beta.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2208,16 +2208,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.1.tgz", + "integrity": "sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/type-utils": "7.0.1", + "@typescript-eslint/utils": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2233,8 +2233,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -2243,15 +2243,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.1.tgz", + "integrity": "sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/typescript-estree": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", "debug": "^4.3.4" }, "engines": { @@ -2262,7 +2262,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -2271,13 +2271,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.1.tgz", + "integrity": "sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2288,13 +2288,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.1.tgz", + "integrity": "sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.1", + "@typescript-eslint/utils": "7.0.1", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2306,7 +2306,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -2315,9 +2315,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.1.tgz", + "integrity": "sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2328,13 +2328,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.1.tgz", + "integrity": "sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2356,17 +2356,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.1.tgz", + "integrity": "sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/typescript-estree": "7.0.1", "semver": "^7.5.4" }, "engines": { @@ -2377,16 +2377,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.1.tgz", + "integrity": "sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.0.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -3124,14 +3124,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", - "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "set-function-length": "^1.2.0" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -3691,17 +3692,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", - "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", + "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", "dependencies": { "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -3992,6 +3996,17 @@ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "devOptional": true }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -5188,11 +5203,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5537,9 +5552,9 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz", + "integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==", "optional": true, "dependencies": { "agent-base": "^7.1.0", @@ -5563,9 +5578,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz", + "integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -7215,9 +7230,9 @@ } }, "node_modules/node-switchbot": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.0.3.tgz", - "integrity": "sha512-fPmtTN/ba1fG6uW/6Witdju1i9C8WJgUYxR0hfeF09A0HZ6MnNLV01et3kVyCcXyUsdyjthp7qms/h4lFq0mmA==", + "version": "2.1.0-beta.1", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.1.tgz", + "integrity": "sha512-SU5TV4aa51SNnVzLOPra5bMLJWiJBr4XGeP7xPHJeKfd9F421i36yS7nKa9Drc3ogeGcJKhstVCrxjFbSskwPA==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-24" diff --git a/package.json b/package.json index f2cc11c5..e1dbc53a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "SwitchBot", "name": "@switchbot/homebridge-switchbot", - "version": "3.4.0", + "version": "3.5.0", "description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.", "author": { "name": "SwitchBot", @@ -84,12 +84,12 @@ "undici": "^6.6.2" }, "optionalDependencies": { - "node-switchbot": "2.0.3" + "node-switchbot": "2.1.0-beta.1" }, "devDependencies": { "@types/node": "^20.11.17", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", "eslint": "^8.56.0", "homebridge": "^1.7.0", "homebridge-config-ui-x": "4.55.1", From 5ff417ea6352ed4a36915bb6856dccb76d50027f Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 13 Feb 2024 08:18:22 -0600 Subject: [PATCH 02/73] new node-switchbot beta --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5bc96e44..ead8e25e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "node": "^18 || ^20" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.1" + "node-switchbot": "2.1.0-beta.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -7230,9 +7230,9 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.0-beta.1", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.1.tgz", - "integrity": "sha512-SU5TV4aa51SNnVzLOPra5bMLJWiJBr4XGeP7xPHJeKfd9F421i36yS7nKa9Drc3ogeGcJKhstVCrxjFbSskwPA==", + "version": "2.1.0-beta.2", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.2.tgz", + "integrity": "sha512-YIlEhBTfLCIUz4/HAH9hguQCcXevCv0dpnK+GzPFggoftHPV7Spk3L9IY8oUuTrCkU7VVtpn+l8MolumHep4cQ==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-24" diff --git a/package.json b/package.json index e1dbc53a..ec147d0c 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "undici": "^6.6.2" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.1" + "node-switchbot": "2.1.0-beta.2" }, "devDependencies": { "@types/node": "^20.11.17", From 5c9bfaa1d4ad5714c93fef4cb7156182cdb5cf1b Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 3 Apr 2024 23:22:23 -0500 Subject: [PATCH 03/73] add leakdetector --- src/device/waterdetector.ts | 884 ++++++++++++++++++++++++++++++++++++ src/platform.ts | 65 +++ src/settings.ts | 7 + 3 files changed, 956 insertions(+) create mode 100644 src/device/waterdetector.ts diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts new file mode 100644 index 00000000..faeea373 --- /dev/null +++ b/src/device/waterdetector.ts @@ -0,0 +1,884 @@ +import { hostname } from 'os'; +import { request } from 'undici'; +import { sleep } from '../utils.js'; +import { MqttClient } from 'mqtt'; +import asyncmqtt from 'async-mqtt'; +import { interval, Subject } from 'rxjs'; +import { skipWhile } from 'rxjs/operators'; +import { SwitchBotPlatform } from '../platform.js'; +import { Service, PlatformAccessory, Units, CharacteristicValue, API, Logging, HAP } from 'homebridge'; +import { device, devicesConfig, serviceData, temperature, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; + +/** + * Platform Accessory + * An instance of this class is created for each accessory your platform registers + * Each accessory may expose multiple services of different service types. + */ +export class WaterDetector { + public readonly api: API; + public readonly log: Logging; + public readonly config!: SwitchBotPlatformConfig; + protected readonly hap: HAP; + // Services + leakService?: Service; + batteryService: Service; + humidityservice?: Service; + temperatureservice?: Service; + + // Characteristic Values + StatusActive!: CharacteristicValue; + LeakDetected!: CharacteristicValue; + BatteryLevel!: CharacteristicValue; + ChargingState!: CharacteristicValue; + StatusLowBattery!: CharacteristicValue; + FirmwareRevision!: CharacteristicValue; + CurrentTemperature!: CharacteristicValue; + CurrentRelativeHumidity!: CharacteristicValue; + + // OpenAPI Status + OpenAPI_BatteryLevel: deviceStatus['battery']; + OpenAPI_FirmwareRevision: deviceStatus['version']; + OpenAPI_CurrentTemperature: deviceStatus['temperature']; + OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; + + // BLE Status + BLE_Celsius!: temperature['c']; + BLE_Fahrenheit!: temperature['f']; + BLE_BatteryLevel!: serviceData['battery']; + BLE_CurrentTemperature!: serviceData['temperature']; + BLE_CurrentRelativeHumidity!: serviceData['humidity']; + + // BLE Others + BLE_IsConnected?: boolean; + + //MQTT stuff + mqttClient: MqttClient | null = null; + + // EVE history service handler + historyService?: any; + + // Config + scanDuration!: number; + deviceLogging!: string; + deviceRefreshRate!: number; + + // Updates + WaterDetectorUpdateInProgress!: boolean; + doWaterDetectorUpdate: Subject; + + // Connection + private readonly OpenAPI: boolean; + private readonly BLE: boolean; + + constructor( + private readonly platform: SwitchBotPlatform, + private accessory: PlatformAccessory, + public device: device & devicesConfig, + ) { + this.api = this.platform.api; + this.log = this.platform.log; + this.config = this.platform.config; + this.hap = this.api.hap; + // Connection + this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; + this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + // default placeholders + this.deviceLogs(device); + this.scan(device); + this.refreshRate(device); + this.deviceContext(); + this.setupHistoryService(device); + this.setupMqtt(device); + this.deviceConfig(device); + + // this is subject we use to track when we need to POST changes to the SwitchBot API + this.doWaterDetectorUpdate = new Subject(); + this.WaterDetectorUpdateInProgress = false; + + // Retrieve initial values and updateHomekit + this.refreshStatus(); + + // set accessory information + accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') + .setCharacteristic(this.hap.Characteristic.Model, 'WoWaterDetector') + .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + + // Leak Sensor Service + if (device.waterdetector?.hide_leak) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); + this.leakService = this.accessory.getService(this.hap.Service.LeakSensor); + accessory.removeService(this.leakService!); + } else if (!this.leakService) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Leak Sensor Service`); + const leakService = `${accessory.displayName} Leak Sensor`; + (this.leakService = this.accessory.getService(this.hap.Service.LeakSensor) + || this.accessory.addService(this.hap.Service.LeakSensor)), leakService; + + this.leakService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Leak Sensor`); + if (!this.leakService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { + this.leakService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Leak Sensor`); + } + } else { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Leak Sensor Service Not Added`); + } + + // Temperature Sensor Service + if (device.waterdetector?.hide_temperature) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor); + accessory.removeService(this.temperatureservice!); + } else if (!this.temperatureservice) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); + const temperatureservice = `${accessory.displayName} Temperature Sensor`; + (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice; + + this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); + if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { + this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); + } + this.temperatureservice + .getCharacteristic(this.hap.Characteristic.CurrentTemperature) + .setProps({ + unit: Units['CELSIUS'], + validValueRanges: [-273.15, 100], + minValue: -273.15, + maxValue: 100, + minStep: 0.1, + }) + .onGet(() => { + return this.CurrentTemperature!; + }); + } else { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + } + + // Humidity Sensor Service + if (device.waterdetector?.hide_humidity) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); + this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor); + accessory.removeService(this.humidityservice!); + } else if (!this.humidityservice) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); + const humidityservice = `${accessory.displayName} Humidity Sensor`; + (this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor) + || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityservice; + + this.humidityservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); + if (!this.humidityservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { + this.humidityservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); + } + this.humidityservice + .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) + .setProps({ + minStep: 0.1, + }) + .onGet(() => { + return this.CurrentRelativeHumidity; + }); + } else { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); + } + + // Battery Service + const batteryService = `${accessory.displayName} Battery`; + (this.batteryService = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), batteryService; + + this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); + if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { + this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); + } + this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + + // Retrieve initial values and updateHomekit + this.updateHomeKitCharacteristics(); + + // Start an update interval + interval(this.deviceRefreshRate * 1000) + .pipe(skipWhile(() => this.WaterDetectorUpdateInProgress)) + .subscribe(async () => { + await this.refreshStatus(); + }); + + //regisiter webhook event handler + if (this.device.webhook) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { + try { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + if (context.scale === 'CELSIUS') { + const { temperature, humidity } = context; + const { CurrentTemperature, CurrentRelativeHumidity } = this; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(temperature, humidity) = ' + + `Webhook:(${temperature}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + this.CurrentRelativeHumidity = humidity; + this.CurrentTemperature = temperature; + this.updateHomeKitCharacteristics(); + } + } catch (e: any) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } + } + + /** + * Parse the device status from the SwitchBot api + */ + async parseStatus(): Promise { + if (!this.device.enableCloudService && this.OpenAPI) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); + } else if (this.BLE) { + await this.BLEparseStatus(); + } else if (this.OpenAPI && this.platform.config.credentials?.token) { + await this.openAPIparseStatus(); + } else { + await this.offlineOff(); + this.debugWarnLog( + `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, + ); + } + } + + async BLEparseStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); + + // Battery + this.BatteryLevel = Number(this.BLE_BatteryLevel); + if (this.BatteryLevel < 15) { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + } else { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + } + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + + // Humidity + if (!this.device.waterdetector?.hide_humidity) { + this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + } + + // Current Temperature + if (!this.device.waterdetector?.hide_temperature) { + this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius; + this.CurrentTemperature = this.BLE_Celsius; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + } + } + + async openAPIparseStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); + // Battery + this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); + if (this.BatteryLevel < 15) { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + } else { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + } + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + + // Current Relative Humidity + if (!this.device.waterdetector?.hide_humidity) { + this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + } + + // Current Temperature + if (!this.device.waterdetector?.hide_temperature) { + this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + } + + // BatteryLevel + this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); + if (this.BatteryLevel < 10) { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + } else { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + } + if (Number.isNaN(this.BatteryLevel)) { + this.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + + ` StatusLowBattery: ${this.StatusLowBattery}`); + + // FirmwareRevision + this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; + this.accessory.context.FirmwareRevision = this.FirmwareRevision; + } + + /** + * Asks the SwitchBot API for the latest device information + */ + async refreshStatus(): Promise { + if (!this.device.enableCloudService && this.OpenAPI) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`); + } else if (this.BLE) { + await this.BLERefreshStatus(); + } else if (this.OpenAPI && this.platform.config.credentials?.token) { + await this.openAPIRefreshStatus(); + } else { + await this.offlineOff(); + this.debugWarnLog( + `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`, + ); + } + } + + async BLERefreshStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`); + const switchbot = await this.platform.connectBLE(); + // Convert to BLE Address + this.device.bleMac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + this.getCustomBLEAddress(switchbot); + // Start to monitor advertisement packets + (async () => { + // Start to monitor advertisement packets + await switchbot.startScan({ + model: 'w', + id: this.device.bleMac, + }); + // Set an event handler + switchbot.onadvertisement = (ad: any) => { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); + if (this.device.bleMac === ad.address && ad.serviceData.model === 'w') { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.BLE_CurrentTemperature = ad.serviceData.temperature; + this.BLE_Celsius = ad.serviceData.temperature!.c; + this.BLE_Fahrenheit = ad.serviceData.temperature!.f; + this.BLE_BatteryLevel = ad.serviceData.battery; + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + } + }; + // Wait 1 seconds + await switchbot.wait(this.scanDuration * 1000); + // Stop to monitor + await switchbot.stopScan(); + // Update HomeKit + await this.BLEparseStatus(); + await this.updateHomeKitCharacteristics(); + })(); + /*if (switchbot !== false) { + switchbot + .startScan({ + model: 'w', + id: this.device.bleMac, + }) + .then(async () => { + // Set an event handler + switchbot.onadvertisement = async (ad: ad) => { + this.debugLog( + `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + + ` BLE Address Found: ${ad.address}`, + ); + if (ad.serviceData.humidity! > 0) { + // reject unreliable data + this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity; + } + this.BLE_CurrentTemperature = ad.serviceData.temperature; + this.BLE_Celsius = ad.serviceData.temperature!.c; + this.BLE_Fahrenheit = ad.serviceData.temperature!.f; + this.BLE_BatteryLevel = ad.serviceData.battery; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.debugLog( + `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + + `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + + `battery: ${ad.serviceData.battery}`, + ); + + if (ad.serviceData) { + this.BLE_IsConnected = true; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); + await this.stopScanning(switchbot); + } else { + this.BLE_IsConnected = false; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); + } + }; + // Wait + return await sleep(this.scanDuration * 1000); + }) + .then(async () => { + // Stop to monitor + await this.stopScanning(switchbot); + }) + .catch(async (e: any) => { + this.apiError(e); + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`, + ); + await this.BLERefreshConnection(switchbot); + }); + } else { + await this.BLERefreshConnection(switchbot); + }*/ + } + + async openAPIRefreshStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); + try { + const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); + const deviceStatus: any = await body.json(); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!; + this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!; + this.OpenAPI_BatteryLevel = deviceStatus.body.battery; + this.OpenAPI_FirmwareRevision = deviceStatus.body.version; + this.openAPIparseStatus(); + this.updateHomeKitCharacteristics(); + } else { + this.statusCode(statusCode); + this.statusCode(deviceStatus.statusCode); + } + } catch (e: any) { + this.apiError(e); + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`, + ); + } + } + + /** + * Updates the status for each of the HomeKit Characteristics + */ + async updateHomeKitCharacteristics(): Promise { + const mqttmessage: string[] = []; + const entry = { time: Math.round(new Date().valueOf() / 1000) }; + if (!this.device.waterdetector?.hide_leak) { + if (this.LeakDetected === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); + } else { + if (this.device.mqttURL) { + mqttmessage.push(`"LeakDetected": ${this.LeakDetected}`); + } + if (this.device.history) { + entry['leak'] = this.LeakDetected; + } + this.accessory.context.LeakDetected = this.LeakDetected; + this.leakService?.updateCharacteristic(this.hap.Characteristic.LeakDetected, this.LeakDetected); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LeakDetected: ${this.LeakDetected}`); + } + } + if (!this.device.waterdetector?.hide_humidity) { + if (this.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + } else { + this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; + this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); + this.debugLog( + `${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, + ); + if (this.device.mqttURL) { + mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); + } + if (this.device.history) { + entry['humidity'] = this.CurrentRelativeHumidity; + } + } + } + if (!this.device.waterdetector?.hide_temperature) { + if (this.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + } else { + if (this.device.mqttURL) { + mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); + } + if (this.device.history) { + entry['temp'] = this.CurrentTemperature; + } + this.accessory.context.CurrentTemperature = this.CurrentTemperature; + this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + } + } + if (this.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + } else { + if (this.device.mqttURL) { + mqttmessage.push(`"battery": ${this.BatteryLevel}`); + } + this.accessory.context.BatteryLevel = this.BatteryLevel; + this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + } + if (this.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + } else { + if (this.device.mqttURL) { + mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`); + } + this.accessory.context.StatusLowBattery = this.StatusLowBattery; + this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + } + if (this.device.mqttURL) { + this.mqttPublish(`{${mqttmessage.join(',')}}`); + } + if (Number(this.CurrentRelativeHumidity) > 0) { + // reject unreliable data + if (this.device.history) { + this.historyService?.addEntry(entry); + } + } + } + + /* + * Publish MQTT message for topics of + * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' + */ + mqttPublish(message: any) { + const mac = this.device.deviceId + ?.toLowerCase() + .match(/[\s\S]{1,2}/g) + ?.join(':'); + const options = this.device.mqttPubOptions || {}; + this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); + } + + /* + * Setup MQTT hadler if URL is specified. + */ + async setupMqtt(device: device & devicesConfig): Promise { + if (device.mqttURL) { + try { + const { connectAsync } = asyncmqtt; + this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); + this.mqttClient.on('error', (e: Error) => { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); + }); + } catch (e) { + this.mqttClient = null; + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + } + } + } + + /* + * Setup EVE history graph feature if enabled. + */ + async setupHistoryService(device: device & devicesConfig): Promise { + const mac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); + this.historyService = device.history + ? new this.platform.fakegatoAPI('room', this.accessory, { + log: this.platform.log, + storage: 'fs', + filename: `${hostname().split('.')[0]}_${mac}_persist.json`, + }) + : null; + } + + async stopScanning(switchbot: any) { + switchbot.stopScan(); + if (this.BLE_IsConnected) { + await this.BLEparseStatus(); + await this.updateHomeKitCharacteristics(); + } else { + await this.BLERefreshConnection(switchbot); + } + } + + async getCustomBLEAddress(switchbot: any) { + if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { + (async () => { + // Start to monitor advertisement packets + await switchbot.startScan({ + model: 'i', + }); + // Set an event handler + switchbot.onadvertisement = (ad: any) => { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); + }; + await sleep(10000); + // Stop to monitor + switchbot.stopScan(); + })(); + } + } + + async BLERefreshConnection(switchbot: any): Promise { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + + ` ${JSON.stringify(switchbot)}`); + if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`); + await this.openAPIRefreshStatus(); + } + } + + async scan(device: device & devicesConfig): Promise { + if (device.scanDuration) { + this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; + if (this.BLE) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); + } + } else { + this.scanDuration = this.accessory.context.scanDuration = 1; + if (this.BLE) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); + } + } + } + + async statusCode(statusCode: number): Promise { + switch (statusCode) { + case 151: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); + break; + case 152: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); + break; + case 160: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); + break; + case 161: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); + this.offlineOff(); + break; + case 171: + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + + `Hub: ${this.device.hubDeviceId}`, + ); + this.offlineOff(); + break; + case 190: + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command format is invalid, statusCode: ${statusCode}`, + ); + break; + case 100: + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); + break; + case 200: + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); + break; + case 400: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` + + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); + break; + case 401: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` + + `but the request has not been authenticated, statusCode: ${statusCode}`); + break; + case 403: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` + + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); + break; + case 404: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` + + `statusCode: ${statusCode}`); + break; + case 406: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` + + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); + break; + case 415: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` + + `header that is not supported by the server, statusCode: ${statusCode}`); + break; + case 422: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); + break; + case 429: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` + + `requests allowed for a given time window, statusCode: ${statusCode}`); + break; + case 500: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` + + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); + break; + default: + this.infoLog( + `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, + ); + } + } + + async offlineOff(): Promise { + if (this.device.offline) { + await this.deviceContext(); + await this.updateHomeKitCharacteristics(); + } + } + + async apiError(e: any): Promise { + if (!this.device.waterdetector?.hide_leak) { + this.leakService?.updateCharacteristic(this.hap.Characteristic.LeakDetected, e); + } + if (!this.device.waterdetector?.hide_humidity) { + this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + } + if (!this.device.waterdetector?.hide_temperature) { + this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); + } + this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + } + + async deviceContext() { + 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; + } + if (this.BatteryLevel === undefined) { + this.BatteryLevel = 100; + } else { + this.BatteryLevel = this.accessory.context.BatteryLevel; + } + if (this.StatusLowBattery === undefined) { + this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.accessory.context.StatusLowBattery = this.StatusLowBattery; + } else { + this.StatusLowBattery = this.accessory.context.StatusLowBattery; + } + if (this.FirmwareRevision === undefined) { + this.FirmwareRevision = this.platform.version; + this.accessory.context.FirmwareRevision = this.FirmwareRevision; + } + } + + async refreshRate(device: device & devicesConfig): Promise { + if (device.refreshRate) { + this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); + } else if (this.platform.config.options!.refreshRate) { + this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); + } + } + + async deviceConfig(device: device & devicesConfig): Promise { + let config = {}; + if (device.waterdetector) { + config = device.waterdetector; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.mqttURL !== undefined) { + config['mqttURL'] = device.mqttURL; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.refreshRate !== undefined) { + config['refreshRate'] = device.refreshRate; + } + if (device.scanDuration !== undefined) { + config['scanDuration'] = device.scanDuration; + } + if (device.webhook !== undefined) { + config['webhook'] = device.webhook; + } + if (Object.entries(config).length !== 0) { + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + } + } + + async deviceLogs(device: device & devicesConfig): Promise { + if (this.platform.debugMode) { + this.deviceLogging = this.accessory.context.logging = 'debugMode'; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); + } else if (device.logging) { + this.deviceLogging = this.accessory.context.logging = device.logging; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); + } else if (this.platform.config.options?.logging) { + this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); + } else { + this.deviceLogging = this.accessory.context.logging = 'standard'; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); + } + } + + /** + * Logging for Device + */ + infoLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.info(String(...log)); + } + } + + warnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.warn(String(...log)); + } + } + + debugWarnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.platform.log.warn('[DEBUG]', String(...log)); + } + } + } + + errorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.error(String(...log)); + } + } + + debugErrorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.platform.log.error('[DEBUG]', String(...log)); + } + } + } + + debugLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging === 'debug') { + this.platform.log.info('[DEBUG]', String(...log)); + } else { + this.platform.log.debug(String(...log)); + } + } + } + + enablingDeviceLogging(): boolean { + return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + } +} diff --git a/src/platform.ts b/src/platform.ts index c35f4378..0ec30834 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -13,6 +13,7 @@ import { Hub } from './device/hub.js'; import { Contact } from './device/contact.js'; import { Curtain } from './device/curtain.js'; import { IOSensor } from './device/iosensor.js'; +import { WaterDetector } from './device/waterdetector.js'; import { MeterPlus } from './device/meterplus.js'; import { ColorBulb } from './device/colorbulb.js'; import { CeilingLight } from './device/ceilinglight.js'; @@ -654,6 +655,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createIOSensor(device); break; + case 'Water Detector': + this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); + this.createWaterDetector(device); + break; case 'Motion Sensor': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createMotion(device); @@ -1150,6 +1155,66 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } + private async createWaterDetector(device: device & devicesConfig) { + const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`); + + // see if an accessory with the same uuid has already been registered and restored from + // the cached devices we stored in the `configureAccessory` method above + const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid); + + if (existingAccessory) { + // the accessory already exists + if (await this.registerDevice(device)) { + // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: + existingAccessory.context.model = device.deviceType; + existingAccessory.context.deviceID = device.deviceId; + existingAccessory.displayName = device.configDeviceName || device.deviceName; + if (device.firmware) { + existingAccessory.context.FirmwareRevision = device.firmware; + } + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; + this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); + existingAccessory.context.connectionType = await this.connectionType(device); + this.api.updatePlatformAccessories([existingAccessory]); + // create the accessory handler for the restored accessory + // this is imported from `platformAccessory.ts` + new WaterDetector(this, existingAccessory, device); + this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`); + } else { + this.unregisterPlatformAccessories(existingAccessory); + } + } else if (await this.registerDevice(device)) { + // the accessory does not yet exist, so we need to create it + if (!device.external) { + this.infoLog(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); + } + + // create a new accessory + const accessory = new this.api.platformAccessory(device.deviceName, uuid); + + // store a copy of the device object in the `accessory.context` + // the `context` property can be used to store any data about the accessory you may need + accessory.context.device = device; + accessory.context.model = device.deviceType; + accessory.context.deviceID = device.deviceId; + if (device.firmware) { + accessory.context.FirmwareRevision = device.firmware; + } + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; + accessory.context.connectionType = await this.connectionType(device); + // create the accessory handler for the newly create accessory + // this is imported from `platformAccessory.ts` + new WaterDetector(this, accessory, device); + this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`); + + // publish device externally or link the accessory to your platform + this.externalOrPlatform(device, accessory); + this.accessories.push(accessory); + } else { + this.debugLog(`Device not registered: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); + } + } + private async createMotion(device: device & devicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`); diff --git a/src/settings.ts b/src/settings.ts index 59f89635..2292f851 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -72,6 +72,7 @@ export interface devicesConfig extends device { blindTilt?: blindTilt; contact?: contact; motion?: motion; + waterdetector?: waterdetector; colorbulb?: colorbulb; striplight?: striplight; ceilinglight?: ceilinglight; @@ -138,6 +139,12 @@ export type motion = { set_maxLux?: number; }; +export type waterdetector = { + hide_leak?: boolean; + hide_temperature?: boolean; + hide_humidity?: boolean; +}; + export type colorbulb = { set_minStep?: number; adaptiveLightingShift?: number; From fc465494e870f1d2b63450decb2ffaa43d3a7591 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 3 Apr 2024 23:26:23 -0500 Subject: [PATCH 04/73] updated dependencies --- CHANGELOG.md | 1 + package-lock.json | 1745 +++++++++++++++++++-------------------------- package.json | 22 +- 3 files changed, 736 insertions(+), 1032 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ede68162..2c5cac18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. This projec ## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) ### What's Changed +- Add Support for `Water Detector` - Add BLE support for `Smart Lock` - Housekeeping and updated dependencies. diff --git a/package-lock.json b/package-lock.json index ead8e25e..cdbaf17b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,25 +19,25 @@ ], "license": "ISC", "dependencies": { - "@homebridge/plugin-ui-utils": "^1.0.1", + "@homebridge/plugin-ui-utils": "^1.0.2", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.3", + "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.6.2" + "undici": "^6.11.1" }, "devDependencies": { - "@types/node": "^20.11.17", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "eslint": "^8.56.0", + "@types/node": "^20.12.4", + "@typescript-eslint/eslint-plugin": "^7.5.0", + "@typescript-eslint/parser": "^7.5.0", + "eslint": "^8.57.0", "homebridge": "^1.7.0", - "homebridge-config-ui-x": "4.55.1", - "nodemon": "^3.0.3", - "npm-check-updates": "^16.14.15", + "homebridge-config-ui-x": "4.56.1", + "nodemon": "^3.1.0", + "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.4.3" }, "engines": { "homebridge": "^1.7.0", @@ -106,19 +106,6 @@ "@abandonware/bluetooth-hci-socket": "^0.5.3-11" } }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.9.tgz", - "integrity": "sha512-oeOFTrYWdWXCvXGB5orvMTJ6gCZ9I6FBjR+M38iKNXCsPxr4xT0RTdg5uz1H7QP8pp74IzPtwritEr+JscqHXQ==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -211,9 +198,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -262,25 +249,22 @@ "dev": true }, "node_modules/@fastify/busboy": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", - "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true, - "dependencies": { - "text-decoding": "^1.0.0" - }, "engines": { "node": ">=14" } }, "node_modules/@fastify/cors": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.4.2.tgz", - "integrity": "sha512-IVynbcPG9eWiJ0P/A1B+KynmiU/yTYbu3ooBUSIeHfca/N1XLb9nIJVCws+YTr2q63MA8Y6QLeXQczEv4npM9g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", + "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==", "dev": true, "dependencies": { "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.5" + "mnemonist": "0.39.6" } }, "node_modules/@fastify/deepmerge": { @@ -352,16 +336,14 @@ "dev": true }, "node_modules/@fastify/multipart": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.0.0.tgz", - "integrity": "sha512-xaH1pGIqYnIJjYs5qG6ryhPSFnWuJIfSXYqEUtzmcyREkMk0SwONd2y+SZ9JXfDmETAC/Ogtc/SRbz+AjZhCkw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.2.0.tgz", + "integrity": "sha512-OZ8nsyyoS2TV7Yeu3ZdrdDGsKUTAbfjrKC9jSxGgT2qdgek+BxpWX31ZubTrWMNZyU5xwk4ox6AvTjAbYWjrWg==", "dev": true, "dependencies": { - "@fastify/busboy": "^1.0.0", + "@fastify/busboy": "^2.1.0", "@fastify/deepmerge": "^1.0.0", "@fastify/error": "^3.0.0", - "@fastify/swagger": "^8.3.1", - "@fastify/swagger-ui": "^1.8.0", "fastify-plugin": "^4.0.0", "secure-json-parse": "^2.4.0", "stream-wormhole": "^1.1.0" @@ -381,43 +363,17 @@ } }, "node_modules/@fastify/static": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz", - "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.2.tgz", + "integrity": "sha512-5opbHpZj29EGVBNgELW6gDkueiFWxjLsLVQQCgKencJctq0aqk3vBlkO97z5It4zaSAb3FXOeAxm7KP2tL/hQA==", "dev": true, "dependencies": { "@fastify/accept-negotiator": "^1.0.0", "@fastify/send": "^2.0.0", "content-disposition": "^0.5.3", "fastify-plugin": "^4.0.0", - "glob": "^8.0.1", - "p-limit": "^3.1.0" - } - }, - "node_modules/@fastify/swagger": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.14.0.tgz", - "integrity": "sha512-sGiznEb3rl6pKGGUZ+JmfI7ct5cwbTQGo+IjewaTvtzfrshnryu4dZwEsjw0YHABpBA+kCz3kpRaHB7qpa67jg==", - "dev": true, - "dependencies": { - "fastify-plugin": "^4.0.0", - "json-schema-resolver": "^2.0.0", - "openapi-types": "^12.0.0", - "rfdc": "^1.3.0", - "yaml": "^2.2.2" - } - }, - "node_modules/@fastify/swagger-ui": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.2.tgz", - "integrity": "sha512-f2mRqtblm6eRAFQ3e8zSngxVNEtiYY7rISKQVjPA++ZsWc5WYlPVTb6Bx0G/zy0BIoucNqDr/Q2Vb/kTYkOq1A==", - "dev": true, - "dependencies": { - "@fastify/static": "^6.0.0", - "fastify-plugin": "^4.0.0", - "openapi-types": "^12.0.2", - "rfdc": "^1.3.0", - "yaml": "^2.2.2" + "fastq": "^1.17.0", + "glob": "^10.3.4" } }, "node_modules/@gar/promisify": { @@ -462,6 +418,19 @@ "dbus2js": "bin/dbus2js.js" } }, + "node_modules/@homebridge/hap-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@homebridge/hap-client/-/hap-client-1.10.0.tgz", + "integrity": "sha512-jbCfsrT97EF9IkW6Wj9fq0ALeSa1y+3UtZ2V+QsacHBd4cfrBr8S5UvlgU+zemctb/EllxMA8S92XqWg5yL2UA==", + "dev": true, + "dependencies": { + "axios": "^1.6.8", + "bonjour-service": "^1.2.1", + "decamelize": "^5.0.1", + "inflection": "^3.0.0", + "source-map-support": "^0.5.21" + } + }, "node_modules/@homebridge/long": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/@homebridge/long/-/long-5.2.1.tgz", @@ -480,9 +449,9 @@ } }, "node_modules/@homebridge/plugin-ui-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.1.tgz", - "integrity": "sha512-Qxpu+HTb5F3tz6iV+gls/snzKPcP/9lOHwoV8IpJlXOeVWj3QMeMuw19dHH8ggLPMm4GaKuObrWjU9yOMENKkw==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.2.tgz", + "integrity": "sha512-QWjk3TvqJTkInVb0ihlVotLfqbtqycnZ+rWYN7qTLomxZcPyU5cuMGvBhKYx+AaaPS+yGd0vhGdJDj/UlKJp/Q==" }, "node_modules/@homebridge/put": { "version": "0.0.8", @@ -543,9 +512,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -577,29 +546,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "devOptional": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "devOptional": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -616,9 +562,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" @@ -641,9 +587,9 @@ } }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "node_modules/@lukeed/csprng": { "version": "1.1.0", @@ -765,22 +711,27 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, "node_modules/@nestjs/axios": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz", - "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.2.tgz", + "integrity": "sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==", "dev": true, "peerDependencies": { "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", "axios": "^1.3.1", - "reflect-metadata": "^0.1.12", "rxjs": "^6.0.0 || ^7.0.0" } }, "node_modules/@nestjs/common": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.0.tgz", - "integrity": "sha512-DGv34UHsZBxCM3H5QGE2XE/+oLJzz5+714JQjBhjD9VccFlQs3LRxo/epso4l7nJIiNlZkPyIUC8WzfU/5RTsQ==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.7.tgz", + "integrity": "sha512-gKFtFzcJznrwsRYjtNZoPAvSOPYdNgxbTYoAyLTpoy393cIKgLmJTHu6ReH8/qIB9AaZLdGaFLkx98W/tFWFUw==", "dev": true, "dependencies": { "iterare": "1.2.1", @@ -794,7 +745,7 @@ "peerDependencies": { "class-transformer": "*", "class-validator": "*", - "reflect-metadata": "^0.1.12", + "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "peerDependenciesMeta": { @@ -807,9 +758,9 @@ } }, "node_modules/@nestjs/core": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.0.tgz", - "integrity": "sha512-N06P5ncknW/Pm8bj964WvLIZn2gNhHliCBoAO1LeBvNImYkecqKcrmLbY49Fa1rmMfEM3MuBHeDys3edeuYAOA==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.7.tgz", + "integrity": "sha512-hsdlnfiQ3kgqHL5k7js3CU0PV7hBJVi+LfFMgCkoagRxNMf67z0GFGeOV2jk5d65ssB19qdYsDa1MGVuEaoUpg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -829,7 +780,7 @@ "@nestjs/microservices": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/websockets": "^10.0.0", - "reflect-metadata": "^0.1.12", + "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "peerDependenciesMeta": { @@ -858,15 +809,15 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.4.tgz", - "integrity": "sha512-xl+gUSp0B+ln1VSNoUftlglk8dfpUes3DHGxKZ5knuBxS5g2H/8p9/DSBOYWUfO5f4u9s6ffBPZ71WO+tbe5SA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", "dev": true, "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "class-transformer": "^0.4.0 || ^0.5.0", "class-validator": "^0.13.0 || ^0.14.0", - "reflect-metadata": "^0.1.12" + "reflect-metadata": "^0.1.12 || ^0.2.0" }, "peerDependenciesMeta": { "class-transformer": { @@ -888,16 +839,16 @@ } }, "node_modules/@nestjs/platform-fastify": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-10.3.0.tgz", - "integrity": "sha512-ka4r/cPWM5y/dXoi9dj6pn1o3WLnfImy2bT3aYVasiDsJff2cd3h/ThugwxjdH0BHUpLSPnawEGzADAcO8Fqug==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-10.3.7.tgz", + "integrity": "sha512-J7ICAOC/zTSfJLTWwvVLK9LON+Dw/NdgPQwPBsi3GNIw3TLLXLrQ4WXnHNER8gnXS3sfKOIngRLusMirLqYjEQ==", "dev": true, "dependencies": { - "@fastify/cors": "8.4.2", + "@fastify/cors": "9.0.1", "@fastify/formbody": "7.4.0", "@fastify/middie": "8.3.0", - "fastify": "4.25.1", - "light-my-request": "5.11.0", + "fastify": "4.26.2", + "light-my-request": "5.12.0", "path-to-regexp": "3.2.0", "tslib": "2.6.2" }, @@ -906,7 +857,7 @@ "url": "https://opencollective.com/nest" }, "peerDependencies": { - "@fastify/static": "^6.0.0", + "@fastify/static": "^6.0.0 || ^7.0.0", "@fastify/view": "^7.0.0 || ^8.0.0", "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0" @@ -921,12 +872,12 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.0.tgz", - "integrity": "sha512-Rdpk9OdsvJfsmVRtg4/0+cUdvOgBEb3F4zo2r4SBgxb0eaR3BHbhbXTJH/U7NvREIvvYbtSNoWI+h2taUEkXwg==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.7.tgz", + "integrity": "sha512-T9VbVgEUnbid/RiywN9/8YQ8pAGDP++0nX73l4kIWeDWkz5DEh4aLB7O/JvLA3/xRHdjTZ4RiRZazwqSWi1Sog==", "dev": true, "dependencies": { - "socket.io": "4.7.2", + "socket.io": "4.7.5", "tslib": "2.6.2" }, "funding": { @@ -940,24 +891,25 @@ } }, "node_modules/@nestjs/swagger": { - "version": "7.1.17", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.17.tgz", - "integrity": "sha512-ASCxBrvMEN2o/8vEEmrIPMNzrr/hVi7QIR4y1oNYvoBNXHuwoF1VSI3+4Rq/3xmwVnVveJxHlBIs2u5xY9VgGQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.3.1.tgz", + "integrity": "sha512-LUC4mr+5oAleEC/a2j8pNRh1S5xhKXJ1Gal5ZdRjt9XebQgbngXCdW7JTA9WOEcwGtFZN9EnKYdquzH971LZfw==", "dev": true, "dependencies": { - "@nestjs/mapped-types": "2.0.4", + "@microsoft/tsdoc": "^0.14.2", + "@nestjs/mapped-types": "2.0.5", "js-yaml": "4.1.0", "lodash": "4.17.21", "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.10.3" + "swagger-ui-dist": "5.11.2" }, "peerDependencies": { - "@fastify/static": "^6.0.0", + "@fastify/static": "^6.0.0 || ^7.0.0", "@nestjs/common": "^9.0.0 || ^10.0.0", "@nestjs/core": "^9.0.0 || ^10.0.0", "class-transformer": "*", "class-validator": "*", - "reflect-metadata": "^0.1.12" + "reflect-metadata": "^0.1.12 || ^0.2.0" }, "peerDependenciesMeta": { "@fastify/static": { @@ -972,9 +924,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.0.tgz", - "integrity": "sha512-1cqh46s4iHLytExSWcvp/58pMZFAT7pQ59IAYQCSZz5xFq0lEGxd36C982KyROQIHfno8E+FWm71UhgVTwKsyA==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.7.tgz", + "integrity": "sha512-iYdsWiRNPUy0XzPoW44bx2MW1griuraTr5fNhoe2rUSNO0mEW1aeXp4v56KeZDLAss31WbeckC5P3N223Fys5g==", "dev": true, "dependencies": { "iterare": "1.2.1", @@ -985,7 +937,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-socket.io": "^10.0.0", - "reflect-metadata": "^0.1.12", + "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "peerDependenciesMeta": { @@ -1030,16 +982,16 @@ } }, "node_modules/@npmcli/agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.1.tgz", - "integrity": "sha512-H4FrOVtNyWC8MUwL3UfjOsAihHvT1Pe8POj3JvjXhSTJipsZMtgUALCT4mGyYZNxymkUfOw3PUj6dE4QPp6osQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", "optional": true, "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -1377,6 +1329,12 @@ "node": ">=10" } }, + "node_modules/@npmcli/run-script/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/@npmcli/run-script/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1641,6 +1599,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@npmcli/run-script/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/@npmcli/run-script/node_modules/socks-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", @@ -1667,6 +1631,20 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/run-script/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@npmcli/run-script/node_modules/unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -1771,29 +1749,6 @@ "@otplib/plugin-thirty-two": "^12.0.1" } }, - "node_modules/@oznu/hap-client": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@oznu/hap-client/-/hap-client-1.9.0.tgz", - "integrity": "sha512-dtx8SNLzT62VVwN2YwBVooLyKBrBphddVvYda8CqY5BHLzjK8SMDOF63t2nvp+MLwzWXKfpmcX1w+qS3V8J9Gw==", - "dev": true, - "dependencies": { - "axios": "^0.27.2", - "bonjour-service": "^1.0.12", - "decamelize": "^3.2.0", - "inflection": "^1.13.2", - "source-map-support": "^0.5.19" - } - }, - "node_modules/@oznu/hap-client/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1915,28 +1870,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@sigstore/sign/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@sigstore/sign/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -2099,9 +2032,9 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "node_modules/@tsconfig/node12": { @@ -2181,18 +2114,24 @@ } }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", + "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/semver": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", - "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/semver-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", + "integrity": "sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==", "dev": true }, "node_modules/@types/validator": { @@ -2208,16 +2147,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.1.tgz", - "integrity": "sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", + "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.0.1", - "@typescript-eslint/type-utils": "7.0.1", - "@typescript-eslint/utils": "7.0.1", - "@typescript-eslint/visitor-keys": "7.0.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/type-utils": "7.5.0", + "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2226,7 +2165,7 @@ "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2243,19 +2182,19 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.1.tgz", - "integrity": "sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.0.1", - "@typescript-eslint/types": "7.0.1", - "@typescript-eslint/typescript-estree": "7.0.1", - "@typescript-eslint/visitor-keys": "7.0.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2271,16 +2210,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.1.tgz", - "integrity": "sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", + "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.1", - "@typescript-eslint/visitor-keys": "7.0.1" + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2288,18 +2227,18 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.1.tgz", - "integrity": "sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", + "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.0.1", - "@typescript-eslint/utils": "7.0.1", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/utils": "7.5.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2315,12 +2254,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.1.tgz", - "integrity": "sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", + "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2328,13 +2267,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.1.tgz", - "integrity": "sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", + "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.1", - "@typescript-eslint/visitor-keys": "7.0.1", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2343,7 +2282,7 @@ "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2356,21 +2295,21 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.1.tgz", - "integrity": "sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", + "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.0.1", - "@typescript-eslint/types": "7.0.1", - "@typescript-eslint/typescript-estree": "7.0.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", "semver": "^7.5.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2381,16 +2320,16 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.1.tgz", - "integrity": "sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", + "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/types": "7.5.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2471,9 +2410,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -2570,6 +2509,26 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2696,9 +2655,12 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -2719,12 +2681,12 @@ } }, "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -2799,12 +2761,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -2824,25 +2789,23 @@ "dev": true }, "node_modules/bonjour-hap": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.4.tgz", - "integrity": "sha512-a76r95/qTAP5hOEZZhRoiosyFSVPPRSVev09Jh8yDf3JDKyrzELLf0vpQCuEXFueb9DcV9UJf2Jv3dktyuPBng==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.5.tgz", + "integrity": "sha512-WsAbvzqveQHukBR4OPTgcZYG0+MxiksihmpMlKHOIOEBw3+iKDeqVPI/LutImZ4DAXBigYYD/JKJEXtZa9ZtLA==", + "dev": true, "dependencies": { "array-flatten": "^2.1.2", - "deep-equal": "^2.0.5", - "ip": "^1.1.8", + "deep-equal": "^2.2.3", "multicast-dns": "^7.2.5", "multicast-dns-service-types": "^1.1.0" } }, "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -2869,18 +2832,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/boxen/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -2893,44 +2844,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/boxen/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/boxen/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -3053,28 +2966,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/cacache/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "optional": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/lru-cache": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", @@ -3256,14 +3147,14 @@ "dev": true }, "node_modules/class-validator": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", - "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", "dev": true, "dependencies": { - "@types/validator": "^13.7.10", - "libphonenumber-js": "^1.10.14", - "validator": "^13.7.0" + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" } }, "node_modules/clean-stack": { @@ -3312,9 +3203,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", + "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -3326,6 +3217,26 @@ "@colors/colors": "1.5.0" } }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -3469,25 +3380,14 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" } }, - "node_modules/core-js-pure": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", - "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3589,15 +3489,15 @@ } }, "node_modules/decamelize": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", - "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", "dev": true, - "dependencies": { - "xregexp": "^4.2.4" - }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decompress-response": { @@ -3692,14 +3592,13 @@ } }, "node_modules/define-data-property": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", - "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3749,9 +3648,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "devOptional": true, "engines": { "node": ">=8" @@ -3778,12 +3677,6 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -3874,14 +3767,14 @@ } }, "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" + "stream-shift": "^1.0.2" } }, "node_modules/eastasianwidth": { @@ -3899,9 +3792,9 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "devOptional": true }, "node_modules/encoding": { @@ -4065,16 +3958,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4360,9 +4253,9 @@ "dev": true }, "node_modules/fast-json-stringify": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.12.0.tgz", - "integrity": "sha512-7Nnm9UPa7SfHRbHVA1kJQrGXCRzB7LMlAAqHXQFkEQqueJm1V8owm0FsE/2Do55/4CcdhwiLQERaKomOnKQkyA==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.13.0.tgz", + "integrity": "sha512-XjTDWKHP3GoMQUOfnjYUbqeHeEt+PvYgvBdG2fRSmYaORILbSr8xTJvZX+w1YSAP5pw2NwKrGRmQleYueZEoxw==", "dev": true, "dependencies": { "@fastify/merge-json-schemas": "^0.1.0", @@ -4418,9 +4311,9 @@ } }, "node_modules/fast-redact": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", - "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", "dev": true, "engines": { "node": ">=6" @@ -4448,19 +4341,29 @@ "dev": true }, "node_modules/fastify": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.1.tgz", - "integrity": "sha512-D8d0rv61TwqoAS7lom2tvIlgVMlx88lLsiwXyWNjA7CU/LC/mx/Gp2WAlC0S/ABq19U+y/aRvYFG5xLUu2aMrg==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.2.tgz", + "integrity": "sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "dependencies": { "@fastify/ajv-compiler": "^3.5.0", "@fastify/error": "^3.4.0", "@fastify/fast-json-stringify-compiler": "^4.3.0", "abstract-logging": "^2.0.1", - "avvio": "^8.2.1", + "avvio": "^8.3.0", "fast-content-type-parse": "^1.1.0", "fast-json-stringify": "^5.8.0", - "find-my-way": "^7.7.0", + "find-my-way": "^8.0.0", "light-my-request": "^5.11.0", "pino": "^8.17.0", "process-warning": "^3.0.0", @@ -4511,9 +4414,9 @@ } }, "node_modules/find-my-way": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz", - "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz", + "integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -4612,15 +4515,15 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -4661,18 +4564,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "devOptional": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -4885,15 +4776,42 @@ "node": ">=10" } }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gaxios": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.2.0.tgz", - "integrity": "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.4.0.tgz", + "integrity": "sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA==", "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", - "node-fetch": "^2.6.9" + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" }, "engines": { "node": ">=14" @@ -4960,19 +4878,22 @@ "dev": true }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "devOptional": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4990,18 +4911,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -5062,9 +4971,9 @@ } }, "node_modules/google-auth-library": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.6.3.tgz", - "integrity": "sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.7.0.tgz", + "integrity": "sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A==", "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -5078,9 +4987,9 @@ } }, "node_modules/googleapis": { - "version": "133.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-133.0.0.tgz", - "integrity": "sha512-6xyc49j+x7N4smawJs/q1i7mbSkt6SYUWWd9RbsmmDW7gRv+mhwZ4xT+XkPihZcNyo/diF//543WZq4szdS74w==", + "version": "134.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-134.0.0.tgz", + "integrity": "sha512-o8LhD1754W6MHWtpwAPeP1WUHgNxuMxCnLMDFlMKAA5kCMTNqX9/eaTXnkkAIv6YRfoKMQ6D1vyR6/biXuhE9g==", "dependencies": { "google-auth-library": "^9.0.0", "googleapis-common": "^7.0.0" @@ -5090,13 +4999,13 @@ } }, "node_modules/googleapis-common": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.0.1.tgz", - "integrity": "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.1.0.tgz", + "integrity": "sha512-p3KHiWDBBWJEXk6SYauBEvxw5+UmRy7k2scxGtsNv9eHsTbpopJ3/7If4OrNnzJ9XMLg3IlyQXpVp8YPQsStiw==", "dependencies": { "extend": "^3.0.2", "gaxios": "^6.0.3", - "google-auth-library": "^9.0.0", + "google-auth-library": "^9.7.0", "qs": "^6.7.0", "url-template": "^2.0.8", "uuid": "^9.0.0" @@ -5166,9 +5075,9 @@ } }, "node_modules/hap-nodejs": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.1.tgz", - "integrity": "sha512-hJuGyjng2jlzhZsviWCldaokT7l7BE3iGmWdlE6DNmQFDTmiBN3deNksAZ2nt7qp5jYEv7ZUvW7WBZqJsLh3ww==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.2.tgz", + "integrity": "sha512-Adapr973wOctCQwyj0hycnID34BkPyt5cc0fATQamuRN8TKBPipR5I/u8rE+G53UA9vtMhsTGJjWfJvzBAMs9w==", "dev": true, "dependencies": { "@homebridge/ciao": "^1.1.5", @@ -5214,9 +5123,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -5268,9 +5177,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -5279,13 +5188,13 @@ } }, "node_modules/hb-lib-tools": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.2.tgz", - "integrity": "sha512-xFU9vxH4Ap3GoZ1zRBYGNCGvRz6lFh0ylYx0QaoYKcydVLHQKleSJX9g3X/YmgcQNpfj4XFWT7uDB/XDZeW4Nw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", + "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", "dependencies": { - "bonjour-hap": "^3.6.4", + "bonjour-hap": "^3.7.1", "chalk": "^4.1.2", - "semver": "^7.5.4" + "semver": "^7.6.0" }, "bin": { "hap": "cli/hap.js", @@ -5294,7 +5203,18 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.11.0||^20||^18" + "node": "20.12.0||^20||^18" + } + }, + "node_modules/hb-lib-tools/node_modules/bonjour-hap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.7.2.tgz", + "integrity": "sha512-BzOdOSIpXqjE1hejVNhj1T7E5YazPNG7cMOph5jQfzf1nF2yO18FSxuIg2zDMa4tFxhNC5d+U+0hT2bQkC5nTw==", + "dependencies": { + "array-flatten": "^2.1.2", + "deep-equal": "^2.2.3", + "multicast-dns": "^7.2.5", + "multicast-dns-service-types": "^1.1.0" } }, "node_modules/helmet": { @@ -5385,9 +5305,9 @@ } }, "node_modules/homebridge-config-ui-x": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.55.1.tgz", - "integrity": "sha512-XojwCOEB6LsJeYt6NFakXGCIs9WFA/xiraVhC1R1+//bQPmv1T1F3sZvrPxp1UTBLKxoxT37SMdtq0us8pRyaw==", + "version": "4.56.1", + "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.56.1.tgz", + "integrity": "sha512-VjOyCZNQG0qLi1GzIh2GSTi2aWtJlx0XZ9yGuupKw0GaY3SvGwLU9900LPqa16OzuPRs6P5BXwS29Gv7r2Ev4w==", "dev": true, "funding": [ { @@ -5401,28 +5321,27 @@ ], "dependencies": { "@fastify/helmet": "11.1.1", - "@fastify/multipart": "8.0.0", - "@fastify/static": "6.12.0", + "@fastify/multipart": "8.2.0", + "@fastify/static": "7.0.2", + "@homebridge/hap-client": "1.10.0", "@homebridge/node-pty-prebuilt-multiarch": "0.11.12", - "@nestjs/axios": "3.0.1", - "@nestjs/common": "10.3.0", - "@nestjs/core": "10.3.0", + "@nestjs/axios": "3.0.2", + "@nestjs/common": "10.3.7", + "@nestjs/core": "10.3.7", "@nestjs/jwt": "10.2.0", "@nestjs/passport": "10.0.3", - "@nestjs/platform-fastify": "10.3.0", - "@nestjs/platform-socket.io": "10.3.0", - "@nestjs/swagger": "7.1.17", - "@nestjs/websockets": "10.3.0", - "@oznu/hap-client": "1.9.0", - "axios": "1.6.5", + "@nestjs/platform-fastify": "10.3.7", + "@nestjs/platform-socket.io": "10.3.7", + "@nestjs/swagger": "7.3.1", + "@nestjs/websockets": "10.3.7", + "axios": "1.6.8", "bash-color": "0.0.4", - "bonjour-service": "=1.1.1", "buffer-shims": "1.0.0", "class-transformer": "0.5.1", - "class-validator": "0.14.0", - "commander": "11.1.0", + "class-validator": "0.14.1", + "commander": "12.0.0", "dayjs": "1.11.10", - "fastify": "4.25.1", + "fastify": "4.26.2", "fs-extra": "11.2.0", "jsonwebtoken": "9.0.2", "lodash": "4.17.21", @@ -5433,12 +5352,12 @@ "p-limit": "3.1.0", "passport": "0.7.0", "passport-jwt": "4.0.1", - "reflect-metadata": "0.1.14", + "reflect-metadata": "0.2.2", "rxjs": "7.8.1", - "semver": "7.5.4", - "systeminformation": "5.21.22", + "semver": "7.6.0", + "systeminformation": "5.22.7", "tail": "2.2.6", - "tar": "6.2.0", + "tar": "6.2.1", "tcp-port-used": "1.0.2", "unzipper": "0.10.14" }, @@ -5452,12 +5371,12 @@ } }, "node_modules/homebridge-config-ui-x/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/homebridge-config-ui-x/node_modules/fs-extra": { @@ -5474,28 +5393,13 @@ "node": ">=14.14" } }, - "node_modules/homebridge-config-ui-x/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/homebridge-lib": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.3.tgz", - "integrity": "sha512-1G2FR3T+/GpRPYD2JU4dJWgf2OFbX/5dzx8doyVS9htkSbZWN4l+rXSVVU2K2KBidnj3NNOVBj21uw/20XGOgQ==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", + "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.0", - "hb-lib-tools": "~1.2.1" + "@homebridge/plugin-ui-utils": "~1.0.1", + "hb-lib-tools": "~1.2.4" }, "bin": { "hap": "cli/hap.js", @@ -5505,7 +5409,7 @@ }, "engines": { "homebridge": "^1.7.0", - "node": "20.10.0||^20||^18" + "node": "20.12.0||^20||^18" } }, "node_modules/hosted-git-info": { @@ -5552,9 +5456,9 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz", - "integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "optional": true, "dependencies": { "agent-base": "^7.1.0", @@ -5578,9 +5482,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz", - "integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -5706,13 +5610,13 @@ "dev": true }, "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.0.tgz", + "integrity": "sha512-1zEJU1l19SgJlmwqsEyFTbScw/tkMHFenUo//Y0i+XEP83gDFdMvPizAD/WGcE+l1ku12PcTVHQhO6g5E0UCMw==", "dev": true, - "engines": [ - "node >= 0.4.0" - ] + "engines": { + "node": ">=18.0.0" + } }, "node_modules/inflight": { "version": "1.0.6", @@ -5729,9 +5633,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -5750,11 +5654,6 @@ "node": ">= 0.4" } }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -5965,9 +5864,12 @@ "devOptional": true }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6041,19 +5943,25 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6123,20 +6031,26 @@ "dev": true }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6277,23 +6191,6 @@ "fast-deep-equal": "^3.1.3" } }, - "node_modules/json-schema-resolver": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz", - "integrity": "sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "rfdc": "^1.1.4", - "uri-js": "^4.2.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" - } - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6462,28 +6359,22 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.55", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.55.tgz", - "integrity": "sha512-MrTg2JFLscgmTY6/oT9vopYETlgUls/FU6OaeeamGwk4LFxjIgOUML/ZSZICgR0LPYXaonVJo40lzMvaaTJlQA==", + "version": "1.10.59", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.59.tgz", + "integrity": "sha512-HeTsOrDF/hWhEiKqZVwg9Cqlep5x2T+IYDENvT2VRj3iX8JQ7Y+omENv+AIn0vC8m6GYhivfCed5Cgfw27r5SA==", "dev": true }, "node_modules/light-my-request": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.11.0.tgz", - "integrity": "sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.12.0.tgz", + "integrity": "sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==", "dev": true, "dependencies": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", + "cookie": "^0.6.0", + "process-warning": "^3.0.0", "set-cookie-parser": "^2.4.1" } }, - "node_modules/light-my-request/node_modules/process-warning": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", - "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==", - "dev": true - }, "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -6946,9 +6837,9 @@ "dev": true }, "node_modules/mnemonist": { - "version": "0.39.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.5.tgz", - "integrity": "sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==", + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", + "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", "dev": true, "dependencies": { "obliterator": "^2.0.1" @@ -7019,9 +6910,9 @@ "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==" }, "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", "devOptional": true }, "node_modules/napi-build-utils": { @@ -7052,9 +6943,9 @@ } }, "node_modules/node-abi": { - "version": "3.54.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", - "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==", + "version": "3.57.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", + "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -7101,9 +6992,9 @@ } }, "node_modules/node-gyp": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz", - "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.1.0.tgz", + "integrity": "sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==", "optional": true, "dependencies": { "env-paths": "^2.2.0", @@ -7144,28 +7035,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/node-gyp/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "optional": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/node-gyp/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -7242,9 +7111,9 @@ } }, "node_modules/nodemon": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", - "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", + "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -7373,9 +7242,9 @@ } }, "node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, "engines": { "node": ">=14.16" @@ -7397,11 +7266,12 @@ } }, "node_modules/npm-check-updates": { - "version": "16.14.15", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.15.tgz", - "integrity": "sha512-WH0wJ9j6CP7Azl+LLCxWAYqroT2IX02kRIzgK/fg0rPpMbETgHITWBdOPtrv521xmA3JMgeNsQ62zvVtS/nCmQ==", + "version": "16.14.18", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.18.tgz", + "integrity": "sha512-9iaRe9ohx9ykdbLjPRIYcq1A0RkrPYUx9HmQK1JIXhfxtJCNE/+497H9Z4PGH6GWRALbz5KF+1iZoySK2uSEpQ==", "dev": true, "dependencies": { + "@types/semver-utils": "^1.1.1", "chalk": "^5.3.0", "cli-table3": "^0.6.3", "commander": "^10.0.1", @@ -7511,28 +7381,6 @@ "node": ">=14" } }, - "node_modules/npm-check-updates/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm-check-updates/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -7815,28 +7663,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -7994,12 +7820,12 @@ } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -8071,12 +7897,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "dev": true - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -8264,28 +8084,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/pacote/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/pacote/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -8416,12 +8214,12 @@ } }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "devOptional": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -8483,9 +8281,9 @@ } }, "node_modules/pino": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.18.0.tgz", - "integrity": "sha512-Mz/gKiRyuXu4HnpHgi1YWdHQCoWMufapzooisvFn78zl4dZciAxS+YeRkUxXl1ee/SzU80YCz1zpECCh4oC6Aw==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz", + "integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", @@ -8569,10 +8367,18 @@ "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", "dev": true }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", "dev": true, "dependencies": { "detect-libc": "^2.0.0", @@ -8758,11 +8564,11 @@ } }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -8879,28 +8685,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-package-json/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -8936,15 +8720,9 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "dev": true - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true }, "node_modules/regexp.prototype.flags": { @@ -9057,6 +8835,12 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/ret": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", @@ -9108,28 +8892,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9264,29 +9026,30 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -9326,11 +9089,11 @@ } }, "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -9343,10 +9106,16 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "devOptional": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "devOptional": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/sigstore": { "version": "1.9.0", @@ -9402,28 +9171,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/sigstore/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/sigstore/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -9616,9 +9363,9 @@ } }, "node_modules/socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", "dev": true, "dependencies": { "accepts": "~1.3.4", @@ -9634,11 +9381,12 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", "dev": true, "dependencies": { + "debug": "~4.3.4", "ws": "~8.11.0" } }, @@ -9677,9 +9425,9 @@ } }, "node_modules/socks": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", - "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", "devOptional": true, "dependencies": { "ip-address": "^9.0.5", @@ -9691,12 +9439,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "optional": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", "socks": "^2.7.1" }, @@ -9761,9 +9509,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", - "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -9873,17 +9621,20 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "devOptional": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -9901,6 +9652,39 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "devOptional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "devOptional": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9950,15 +9734,15 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.10.3.tgz", - "integrity": "sha512-fu3aozjxFWsmcO1vyt1q1Ji2kN7KlTd1vHy27E9WgPyXo9nrEzhQPqgxaAjbMsOmb8XFKNGo4Sa3Q+84Fh+pFw==", + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz", + "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==", "dev": true }, "node_modules/systeminformation": { - "version": "5.21.22", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.22.tgz", - "integrity": "sha512-gNHloAJSyS+sKWkwvmvozZ1eHrdVTEsynWMTY6lvLGBB70gflkBQFw8drXXr1oEXY84+Vr9tOOrN8xHZLJSycA==", + "version": "5.22.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.7.tgz", + "integrity": "sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==", "dev": true, "os": [ "darwin", @@ -9991,9 +9775,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "devOptional": true, "dependencies": { "chownr": "^2.0.0", @@ -10113,12 +9897,6 @@ } } }, - "node_modules/text-decoding": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", - "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -10226,9 +10004,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { "node": ">=16" @@ -10334,28 +10112,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/tuf-js/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/tuf-js/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -10522,9 +10278,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10553,12 +10309,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz", - "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.11.1.tgz", + "integrity": "sha512-KyhzaLJnV1qa3BSHdj4AZ2ndqI0QWPxYzaIOio0WzcEJB9gvuysprJSLtpvc2D9mhR9jPDUk7xlJlZbH2KR5iw==", "engines": { "node": ">=18.0" } @@ -10569,14 +10322,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "node_modules/undici/node_modules/@fastify/busboy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", - "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", - "engines": { - "node": ">=14" - } - }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -10743,9 +10488,9 @@ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, "node_modules/usb": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/usb/-/usb-2.11.0.tgz", - "integrity": "sha512-u5+NZ6DtoW8TIBtuSArQGAZZ/K15i3lYvZBAYmcgI+RcDS9G50/KPrUd3CrU8M92ahyCvg5e0gc8BDvr5Hwejg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/usb/-/usb-2.12.1.tgz", + "integrity": "sha512-hgtoSQUFuMXVJBApelpUTiX7ZB83MQCbYeHTBsHftA2JG7YZ76ycwIgKQhkhKqVY76C8K6xJscHpF7Ep0eG3pQ==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -10892,29 +10637,32 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -10932,69 +10680,39 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "dev": true, - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "string-width": "^5.0.1" }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/wrap-ansi": { @@ -11032,6 +10750,26 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -11056,29 +10794,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "devOptional": true - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "devOptional": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -11111,6 +10826,12 @@ "typedarray-to-buffer": "^3.1.5" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", @@ -11165,15 +10886,6 @@ "node": ">=4.0" } }, - "node_modules/xregexp": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz", - "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==", - "dev": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.12.1" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -11187,15 +10899,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index ec147d0c..d6b6c522 100644 --- a/package.json +++ b/package.json @@ -76,27 +76,27 @@ "ir" ], "dependencies": { - "@homebridge/plugin-ui-utils": "^1.0.1", + "@homebridge/plugin-ui-utils": "^1.0.2", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.3", + "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.6.2" + "undici": "^6.11.1" }, "optionalDependencies": { "node-switchbot": "2.1.0-beta.2" }, "devDependencies": { - "@types/node": "^20.11.17", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "eslint": "^8.56.0", + "@types/node": "^20.12.4", + "@typescript-eslint/eslint-plugin": "^7.5.0", + "@typescript-eslint/parser": "^7.5.0", + "eslint": "^8.57.0", "homebridge": "^1.7.0", - "homebridge-config-ui-x": "4.55.1", - "nodemon": "^3.0.3", - "npm-check-updates": "^16.14.15", + "homebridge-config-ui-x": "4.56.1", + "nodemon": "^3.1.0", + "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.4.3" } } From a9ba0395b99d38e46c0996e8701c1b0b0144b51d Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 3 Apr 2024 23:39:37 -0500 Subject: [PATCH 05/73] add waterdetector to config --- config.schema.json | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/config.schema.json b/config.schema.json index 0040962e..b6575dcb 100644 --- a/config.schema.json +++ b/config.schema.json @@ -149,6 +149,12 @@ "WoIOSensor" ] }, + { + "title": "Water Detector", + "enum": [ + "Water Detector" + ] + }, { "title": "Meter", "enum": [ @@ -484,6 +490,32 @@ } } }, + "waterdetector": { + "type": "object", + "properties": { + "hide_leak": { + "title": "Hide Water Detector's Leak Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" + } + }, + "hide_temperature": { + "title": "Hide Water Detector's Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" + } + }, + "hide_humidity": { + "title": "Hide Water Detector's Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" + } + } + } + }, "humidifier": { "type": "object", "properties": { @@ -853,28 +885,28 @@ "type": "string", "placeholder": "192.168.7.1", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, "mqttOptions": { "title": "MQTT Options", "type": "string", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, "mqttPubOptions": { "title": "MQTT Pub Options", "type": "string", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, "history": { "title": "EVE History", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, "firmware": { @@ -1618,6 +1650,9 @@ "options.devices[].bot.pushRatePress", "options.devices[].meter.hide_temperature", "options.devices[].meter.hide_humidity", + "options.devices[].waterdetector.hide_leak", + "options.devices[].waterdetector.hide_temperature", + "options.devices[].waterdetector.hide_humidity", "options.devices[].humidifier.set_minStep", "options.devices[].humidifier.hide_temperature", "options.devices[].curtain.set_minStep", From 2af69d7b1d258eded484af1837598e7c80aebd64 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 9 Apr 2024 10:05:41 -0500 Subject: [PATCH 06/73] add k10+ --- CHANGELOG.md | 3 +- package-lock.json | 159 +++++++++++++++++++++++++--------------------- package.json | 12 ++-- src/platform.ts | 1 + 4 files changed, 96 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c5cac18..03751335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,12 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) -## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) +## [BETA-3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) ### What's Changed - Add Support for `Water Detector` - Add BLE support for `Smart Lock` +- Add `K10+` deviceType Support - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 diff --git a/package-lock.json b/package-lock.json index cdbaf17b..45776311 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,17 +19,17 @@ ], "license": "ISC", "dependencies": { - "@homebridge/plugin-ui-utils": "^1.0.2", + "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.11.1" + "undici": "^6.12.0" }, "devDependencies": { - "@types/node": "^20.12.4", - "@typescript-eslint/eslint-plugin": "^7.5.0", - "@typescript-eslint/parser": "^7.5.0", + "@types/node": "^20.12.6", + "@typescript-eslint/eslint-plugin": "^7.6.0", + "@typescript-eslint/parser": "^7.6.0", "eslint": "^8.57.0", "homebridge": "^1.7.0", "homebridge-config-ui-x": "4.56.1", @@ -37,7 +37,7 @@ "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.4.3" + "typescript": "^5.4.4" }, "engines": { "homebridge": "^1.7.0", @@ -449,9 +449,9 @@ } }, "node_modules/@homebridge/plugin-ui-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.2.tgz", - "integrity": "sha512-QWjk3TvqJTkInVb0ihlVotLfqbtqycnZ+rWYN7qTLomxZcPyU5cuMGvBhKYx+AaaPS+yGd0vhGdJDj/UlKJp/Q==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.3.tgz", + "integrity": "sha512-p2S/czGYNRnRtMICxBUk4Uar+KCezfyxjqfStfxKgykD2082SNayVDncYUK1xRai78EGHCbif9eoyrmDweh4tQ==" }, "node_modules/@homebridge/put": { "version": "0.0.8", @@ -2114,9 +2114,9 @@ } }, "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "version": "20.12.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz", + "integrity": "sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2147,22 +2147,22 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2182,15 +2182,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -2210,13 +2210,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2227,15 +2227,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2254,9 +2254,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2267,19 +2267,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2294,19 +2294,34 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2320,13 +2335,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -10278,9 +10293,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10309,9 +10324,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.11.1.tgz", - "integrity": "sha512-KyhzaLJnV1qa3BSHdj4AZ2ndqI0QWPxYzaIOio0WzcEJB9gvuysprJSLtpvc2D9mhR9jPDUk7xlJlZbH2KR5iw==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.12.0.tgz", + "integrity": "sha512-d87yk8lqSFUYtR5fTFe2frpkMIrUEz+lgoJmhcL+J3StVl+8fj8ytE4lLnJOTPCE12YbumNGzf4LYsQyusdV5g==", "engines": { "node": ">=18.0" } diff --git a/package.json b/package.json index d6b6c522..10660a2c 100644 --- a/package.json +++ b/package.json @@ -76,20 +76,20 @@ "ir" ], "dependencies": { - "@homebridge/plugin-ui-utils": "^1.0.2", + "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.11.1" + "undici": "^6.12.0" }, "optionalDependencies": { "node-switchbot": "2.1.0-beta.2" }, "devDependencies": { - "@types/node": "^20.12.4", - "@typescript-eslint/eslint-plugin": "^7.5.0", - "@typescript-eslint/parser": "^7.5.0", + "@types/node": "^20.12.6", + "@typescript-eslint/eslint-plugin": "^7.6.0", + "@typescript-eslint/parser": "^7.6.0", "eslint": "^8.57.0", "homebridge": "^1.7.0", "homebridge-config-ui-x": "4.56.1", @@ -97,6 +97,6 @@ "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.4.3" + "typescript": "^5.4.4" } } diff --git a/src/platform.ts b/src/platform.ts index 0ec30834..fac224b6 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -691,6 +691,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createColorBulb(device); break; + case 'K10+': case 'WoSweeper': case 'WoSweeperMini': case 'Robot Vacuum Cleaner S1': From d97470dc747284a774b6e5281d894c9e40fbac33 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 10 Apr 2024 15:07:25 -0500 Subject: [PATCH 07/73] waterdetected added to api --- src/device/waterdetector.json | 11 ++ src/device/waterdetector.ts | 235 +++++----------------------------- src/settings.ts | 1 + 3 files changed, 42 insertions(+), 205 deletions(-) create mode 100644 src/device/waterdetector.json diff --git a/src/device/waterdetector.json b/src/device/waterdetector.json new file mode 100644 index 00000000..f24f250b --- /dev/null +++ b/src/device/waterdetector.json @@ -0,0 +1,11 @@ +{ + "statusCode": 100, + "body": { + "deviceId": "XXXXXXXXXXXXXX", + "hubDeviceId": "XXXXXXXXXXXXXX", + "status": 0, + "battery": 100, + "version": "V1.3-1.3" + }, + "message": "success" +} \ No newline at end of file diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index faeea373..c6bf5e98 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -6,8 +6,8 @@ import asyncmqtt from 'async-mqtt'; import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, Units, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, temperature, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; /** * Platform Accessory @@ -32,21 +32,15 @@ export class WaterDetector { ChargingState!: CharacteristicValue; StatusLowBattery!: CharacteristicValue; FirmwareRevision!: CharacteristicValue; - CurrentTemperature!: CharacteristicValue; - CurrentRelativeHumidity!: CharacteristicValue; // OpenAPI Status OpenAPI_BatteryLevel: deviceStatus['battery']; OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; + OpenAPI_LeakDetected: deviceStatus['status']; // BLE Status - BLE_Celsius!: temperature['c']; - BLE_Fahrenheit!: temperature['f']; BLE_BatteryLevel!: serviceData['battery']; - BLE_CurrentTemperature!: serviceData['temperature']; - BLE_CurrentRelativeHumidity!: serviceData['humidity']; + BLE_LeakDetected!: number;//serviceData['status']; // BLE Others BLE_IsConnected?: boolean; @@ -130,28 +124,6 @@ export class WaterDetector { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor); accessory.removeService(this.temperatureservice!); - } else if (!this.temperatureservice) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureservice = `${accessory.displayName} Temperature Sensor`; - (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice; - - this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureservice - .getCharacteristic(this.hap.Characteristic.CurrentTemperature) - .setProps({ - unit: Units['CELSIUS'], - validValueRanges: [-273.15, 100], - minValue: -273.15, - maxValue: 100, - minStep: 0.1, - }) - .onGet(() => { - return this.CurrentTemperature!; - }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); } @@ -161,24 +133,6 @@ export class WaterDetector { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor); accessory.removeService(this.humidityservice!); - } else if (!this.humidityservice) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const humidityservice = `${accessory.displayName} Humidity Sensor`; - (this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityservice; - - this.humidityservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); - if (!this.humidityservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidityservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); - } - this.humidityservice - .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) - .setProps({ - minStep: 0.1, - }) - .onGet(() => { - return this.CurrentRelativeHumidity; - }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); } @@ -210,17 +164,14 @@ export class WaterDetector { this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; - this.updateHomeKitCharacteristics(); - } + const { status } = context; + const { LeakDetected } = this; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(status) = ' + + `Webhook:(${status}), ` + + `current:(${LeakDetected})`); + this.LeakDetected = status; + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); @@ -259,55 +210,31 @@ export class WaterDetector { } this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); - // Humidity - if (!this.device.waterdetector?.hide_humidity) { - this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); - } - - // Current Temperature - if (!this.device.waterdetector?.hide_temperature) { - this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius; - this.CurrentTemperature = this.BLE_Celsius; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); - } + // LeakDetected + this.LeakDetected = this.BLE_LeakDetected!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}%`); } async openAPIparseStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - // Battery + // StatusLowBattery this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 15) { + if (this.BatteryLevel < 10) { this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); - // Current Relative Humidity - if (!this.device.waterdetector?.hide_humidity) { - this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); - } - - // Current Temperature - if (!this.device.waterdetector?.hide_temperature) { - this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); - } - // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; - } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - } if (Number.isNaN(this.BatteryLevel)) { this.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + + // LeakDetected + this.LeakDetected = this.OpenAPI_LeakDetected!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}%`); // FirmwareRevision this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; @@ -347,18 +274,16 @@ export class WaterDetector { (async () => { // Start to monitor advertisement packets await switchbot.startScan({ - model: 'w', + model: 'l', id: this.device.bleMac, }); // Set an event handler switchbot.onadvertisement = (ad: any) => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'w') { + if (this.device.bleMac === ad.address && ad.serviceData.model === 'l') { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; + this.BLE_LeakDetected = ad.serviceData.status; this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); @@ -372,61 +297,6 @@ export class WaterDetector { await this.BLEparseStatus(); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'w', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - if (ad.serviceData.humidity! > 0) { - // reject unreliable data - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity; - } - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + - `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + - `battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { - await this.BLERefreshConnection(switchbot); - }*/ } async openAPIRefreshStatus(): Promise { @@ -442,8 +312,7 @@ export class WaterDetector { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!; - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!; + this.OpenAPI_LeakDetected = deviceStatus.body.status; this.OpenAPI_BatteryLevel = deviceStatus.body.battery; this.OpenAPI_FirmwareRevision = deviceStatus.body.version; this.openAPIparseStatus(); @@ -482,39 +351,6 @@ export class WaterDetector { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LeakDetected: ${this.LeakDetected}`); } } - if (!this.device.waterdetector?.hide_humidity) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); - } else { - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, - ); - if (this.device.mqttURL) { - mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); - } - if (this.device.history) { - entry['humidity'] = this.CurrentRelativeHumidity; - } - } - } - if (!this.device.waterdetector?.hide_temperature) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); - } else { - if (this.device.mqttURL) { - mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); - } - if (this.device.history) { - entry['temp'] = this.CurrentTemperature; - } - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); - } - } if (this.BatteryLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { @@ -538,7 +374,7 @@ export class WaterDetector { if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.CurrentRelativeHumidity) > 0) { + if (Number(this.LeakDetected) > 0) { // reject unreliable data if (this.device.history) { this.historyService?.addEntry(entry); @@ -736,26 +572,15 @@ export class WaterDetector { if (!this.device.waterdetector?.hide_leak) { this.leakService?.updateCharacteristic(this.hap.Characteristic.LeakDetected, e); } - if (!this.device.waterdetector?.hide_humidity) { - this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); - } - if (!this.device.waterdetector?.hide_temperature) { - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } async deviceContext() { - if (this.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; - } else { - this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity; - } - if (this.CurrentTemperature === undefined) { - this.CurrentTemperature = 0; + if (this.LeakDetected === undefined) { + this.LeakDetected = 0; } else { - this.CurrentTemperature = this.accessory.context.CurrentTemperature; + this.LeakDetected = this.accessory.context.LeakDetected; } if (this.BatteryLevel === undefined) { this.BatteryLevel = 100; diff --git a/src/settings.ts b/src/settings.ts index 2292f851..05ad5ced 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -320,6 +320,7 @@ export type deviceStatus = { shaking?: boolean; //determines if the fan is swinging or not. (Used by the following deviceTypes: Smart Fan) shakeCenter?: string; //the fan's swing direction. (Used by the following deviceTypes: Smart Fan) shakeRange?: string; //the fan's swing range, 0~120°. (Used by the following deviceTypes: Smart Fan) + status?: number //the leak status. 0 for no leak, 1 for leak. (Used by the following deviceTypes: Water Detector) }; export type ad = { From 3c2759b708d417ce04f5d242dbabfd20fb6bf524 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 10 Apr 2024 22:15:48 -0500 Subject: [PATCH 08/73] Update waterdetector.ts --- src/device/waterdetector.ts | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index c6bf5e98..cab2d5bc 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -22,8 +22,6 @@ export class WaterDetector { // Services leakService?: Service; batteryService: Service; - humidityservice?: Service; - temperatureservice?: Service; // Characteristic Values StatusActive!: CharacteristicValue; @@ -119,24 +117,6 @@ export class WaterDetector { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Leak Sensor Service Not Added`); } - // Temperature Sensor Service - if (device.waterdetector?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureservice!); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); - } - - // Humidity Sensor Service - if (device.waterdetector?.hide_humidity) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor); - accessory.removeService(this.humidityservice!); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); - } - // Battery Service const batteryService = `${accessory.displayName} Battery`; (this.batteryService = this.accessory.getService(this.hap.Service.Battery) @@ -212,7 +192,7 @@ export class WaterDetector { // LeakDetected this.LeakDetected = this.BLE_LeakDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}%`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); } async openAPIparseStatus(): Promise { @@ -234,7 +214,7 @@ export class WaterDetector { // LeakDetected this.LeakDetected = this.OpenAPI_LeakDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}%`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); // FirmwareRevision this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; From a12f279d4c7c18dffb89c52538fd4dc55a0e7d95 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 10 Apr 2024 22:17:35 -0500 Subject: [PATCH 09/73] remove temp and humid --- config.schema.json | 16 ---------------- src/settings.ts | 2 -- 2 files changed, 18 deletions(-) diff --git a/config.schema.json b/config.schema.json index b6575dcb..8fdb4da4 100644 --- a/config.schema.json +++ b/config.schema.json @@ -499,20 +499,6 @@ "condition": { "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" } - }, - "hide_temperature": { - "title": "Hide Water Detector's Temperature Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" - } - }, - "hide_humidity": { - "title": "Hide Water Detector's Humidity Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" - } } } }, @@ -1651,8 +1637,6 @@ "options.devices[].meter.hide_temperature", "options.devices[].meter.hide_humidity", "options.devices[].waterdetector.hide_leak", - "options.devices[].waterdetector.hide_temperature", - "options.devices[].waterdetector.hide_humidity", "options.devices[].humidifier.set_minStep", "options.devices[].humidifier.hide_temperature", "options.devices[].curtain.set_minStep", diff --git a/src/settings.ts b/src/settings.ts index 05ad5ced..e3fba114 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -141,8 +141,6 @@ export type motion = { export type waterdetector = { hide_leak?: boolean; - hide_temperature?: boolean; - hide_humidity?: boolean; }; export type colorbulb = { From fc3ce00c094347b67a225e540b5c023bc0c58248 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 10 Apr 2024 22:28:38 -0500 Subject: [PATCH 10/73] Update config.schema.json --- config.schema.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config.schema.json b/config.schema.json index 8fdb4da4..9121ae52 100644 --- a/config.schema.json +++ b/config.schema.json @@ -1615,7 +1615,8 @@ "title": "{{ value.configDeviceName || value.deviceId || 'New SwitchBot Device' }}", "expandable": true, "expanded": false, - "orderable": false, + "draggable": true, + "orderable": true, "items": [ "options.devices[].configDeviceName", "options.devices[].deviceId", @@ -1694,7 +1695,8 @@ "title": "{{ value.configDeviceName || value.deviceId || 'New IR Device' }}", "expandable": true, "expanded": false, - "orderable": false, + "draggable": true, + "orderable": true, "items": [ "options.irdevices[].configDeviceName", "options.irdevices[].deviceId", From e4f965ff793ec6c5c4ce791f5b5d409ac13764d3 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:29:39 -0500 Subject: [PATCH 11/73] success logs added --- package-lock.json | 277 +++++++++++++------------------ package.json | 12 +- src/device/blindtilt.ts | 28 +++- src/device/bot.ts | 10 ++ src/device/ceilinglight.ts | 14 ++ src/device/colorbulb.ts | 16 ++ src/device/contact.ts | 6 + src/device/curtain.ts | 10 ++ src/device/hub.ts | 6 + src/device/humidifier.ts | 16 +- src/device/iosensor.ts | 6 + src/device/lightstrip.ts | 14 ++ src/device/lock.ts | 10 ++ src/device/meter.ts | 6 + src/device/meterplus.ts | 6 + src/device/motion.ts | 6 + src/device/plug.ts | 10 ++ src/device/robotvacuumcleaner.ts | 21 ++- src/device/waterdetector.json | 11 -- src/device/waterdetector.ts | 6 + src/irdevice/airconditioner.ts | 7 + src/irdevice/airpurifier.ts | 7 + src/irdevice/camera.ts | 7 + src/irdevice/fan.ts | 7 + src/irdevice/light.ts | 7 + src/irdevice/other.ts | 7 + src/irdevice/tv.ts | 7 + src/irdevice/vacuumcleaner.ts | 7 + src/irdevice/waterheater.ts | 7 + src/platform.ts | 6 + 30 files changed, 367 insertions(+), 193 deletions(-) delete mode 100644 src/device/waterdetector.json diff --git a/package-lock.json b/package-lock.json index 45776311..2ee27735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,20 +24,20 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.12.0" + "undici": "^6.13.0" }, "devDependencies": { - "@types/node": "^20.12.6", - "@typescript-eslint/eslint-plugin": "^7.6.0", - "@typescript-eslint/parser": "^7.6.0", + "@types/node": "^20.12.7", + "@typescript-eslint/eslint-plugin": "^7.7.0", + "@typescript-eslint/parser": "^7.7.0", "eslint": "^8.57.0", - "homebridge": "^1.7.0", + "homebridge": "^1.8.0", "homebridge-config-ui-x": "4.56.1", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.4.4" + "typescript": "^5.4.5" }, "engines": { "homebridge": "^1.7.0", @@ -383,9 +383,9 @@ "dev": true }, "node_modules/@homebridge/ciao": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.1.8.tgz", - "integrity": "sha512-Atn8+vwYtfI/J6nYCOVm4uVBAmiQO4rPi0umVbh766cf/OsVxQ+Qedbo9lxIf15iDsMbBlDV7T1wATdHqI5lXw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.2.0.tgz", + "integrity": "sha512-2Qa8MVC7Q5DKH6iXh6cRvqz9VJYVpVZ+whHKrnr8YdPkXxc67kiQ9IOxMb0ydokDTETBVyXgr1m+HrheBtqDoQ==", "dev": true, "dependencies": { "debug": "^4.3.4", @@ -397,22 +397,22 @@ "ciao-bcs": "lib/bonjour-conformance-testing.js" }, "engines": { - "node": ">=14" + "node": "^18 || ^20" } }, "node_modules/@homebridge/dbus-native": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.1.tgz", - "integrity": "sha512-7xXz3R1W/kcbfQOGp32y4K7etqtowICR1vpx8j85KwPYXbNQrgiZ3zcwDYgDGBWq3FD9xzsW7h4YWJ4vTR2seQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.6.0.tgz", + "integrity": "sha512-xObqQeYHTXmt6wsfj10+krTo4xbzR9BgUfX2aQ+edDC9nc4ojfzLScfXCh3zluAm6UCowKw+AFfXn6WLWUOPkg==", "dev": true, "dependencies": { "@homebridge/long": "^5.2.1", - "@homebridge/put": "~0.0.8", - "event-stream": "^4.0.0", - "hexy": "^0.2.10", + "@homebridge/put": "^0.0.8", + "event-stream": "^4.0.1", + "hexy": "^0.3.5", "minimist": "^1.2.6", - "safe-buffer": "^5.1.1", - "xml2js": "^0.5.0" + "safe-buffer": "^5.1.2", + "xml2js": "^0.6.2" }, "bin": { "dbus2js": "bin/dbus2js.js" @@ -2114,9 +2114,9 @@ } }, "node_modules/@types/node": { - "version": "20.12.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz", - "integrity": "sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2147,16 +2147,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", - "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz", + "integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/type-utils": "7.6.0", - "@typescript-eslint/utils": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/type-utils": "7.7.0", + "@typescript-eslint/utils": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.3.1", @@ -2182,15 +2182,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz", + "integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/typescript-estree": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0", "debug": "^4.3.4" }, "engines": { @@ -2210,13 +2210,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz", + "integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0" + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2227,13 +2227,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", - "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz", + "integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/typescript-estree": "7.7.0", + "@typescript-eslint/utils": "7.7.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2254,9 +2254,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz", + "integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2267,13 +2267,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz", + "integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2294,33 +2294,18 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", - "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz", + "integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.15", "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/typescript-estree": "7.7.0", "semver": "^7.6.0" }, "engines": { @@ -2335,12 +2320,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz", + "integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/types": "7.7.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2804,10 +2789,9 @@ "dev": true }, "node_modules/bonjour-hap": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.5.tgz", - "integrity": "sha512-WsAbvzqveQHukBR4OPTgcZYG0+MxiksihmpMlKHOIOEBw3+iKDeqVPI/LutImZ4DAXBigYYD/JKJEXtZa9ZtLA==", - "dev": true, + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.7.2.tgz", + "integrity": "sha512-BzOdOSIpXqjE1hejVNhj1T7E5YazPNG7cMOph5jQfzf1nF2yO18FSxuIg2zDMa4tFxhNC5d+U+0hT2bQkC5nTw==", "dependencies": { "array-flatten": "^2.1.2", "deep-equal": "^2.2.3", @@ -3299,12 +3283,12 @@ } }, "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "dev": true, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/commist": { @@ -4633,9 +4617,9 @@ "dev": true }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -4643,7 +4627,7 @@ "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -4763,9 +4747,9 @@ } }, "node_modules/futoin-hkdf": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.4.3.tgz", - "integrity": "sha512-K4MIe2xSVRMYxsA4w0ap5fp1C2hA9StA2Ad1JZHX57VMCdHIRB5BSrd1FhuadTQG9MkjggaTCrw7v5XXFyY3/w==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.5.3.tgz", + "integrity": "sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ==", "dev": true, "engines": { "node": ">=8" @@ -5090,24 +5074,24 @@ } }, "node_modules/hap-nodejs": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.2.tgz", - "integrity": "sha512-Adapr973wOctCQwyj0hycnID34BkPyt5cc0fATQamuRN8TKBPipR5I/u8rE+G53UA9vtMhsTGJjWfJvzBAMs9w==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.12.0.tgz", + "integrity": "sha512-W+KPE4kCtudt/WTEHlXLiZmgIC5IgK8TXpUPmp27Qm7TOfKwWVGhKYXWRBEr3bkgSe3CGuGQN8vFeRB//mpfhQ==", "dev": true, "dependencies": { - "@homebridge/ciao": "^1.1.5", - "@homebridge/dbus-native": "^0.5.1", - "bonjour-hap": "~3.6.4", + "@homebridge/ciao": "^1.2.0", + "@homebridge/dbus-native": "^0.6.0", + "bonjour-hap": "^3.7.2", "debug": "^4.3.4", - "fast-srp-hap": "~2.0.4", - "futoin-hkdf": "~1.4.3", + "fast-srp-hap": "^2.0.4", + "futoin-hkdf": "^1.5.3", "node-persist": "^0.0.11", "source-map-support": "^0.5.21", - "tslib": "^2.4.0", + "tslib": "^2.6.2", "tweetnacl": "^1.0.3" }, "engines": { - "node": ">=10.17.0" + "node": "^18 || ^20" } }, "node_modules/has-bigints": { @@ -5221,17 +5205,6 @@ "node": "20.12.0||^20||^18" } }, - "node_modules/hb-lib-tools/node_modules/bonjour-hap": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.7.2.tgz", - "integrity": "sha512-BzOdOSIpXqjE1hejVNhj1T7E5YazPNG7cMOph5jQfzf1nF2yO18FSxuIg2zDMa4tFxhNC5d+U+0hT2bQkC5nTw==", - "dependencies": { - "array-flatten": "^2.1.2", - "deep-equal": "^2.2.3", - "multicast-dns": "^7.2.5", - "multicast-dns-service-types": "^1.1.0" - } - }, "node_modules/helmet": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", @@ -5290,27 +5263,30 @@ } }, "node_modules/hexy": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz", - "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.3.5.tgz", + "integrity": "sha512-UCP7TIZPXz5kxYJnNOym+9xaenxCLor/JyhKieo8y8/bJWunGh9xbhy3YrgYJUQ87WwfXGm05X330DszOfINZw==", "dev": true, "bin": { "hexy": "bin/hexy_cmd.js" + }, + "engines": { + "node": ">=10.4" } }, "node_modules/homebridge": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.7.0.tgz", - "integrity": "sha512-2QikXnmpnFe2s33Q8TeYE5+sXyKHUZ+9l5WfDmpuupHdct6H/G6b6z3HCj+2rlMRKKY5ElLv5XtLoxOcafnL0g==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.0.tgz", + "integrity": "sha512-nPb9rPIUIjJ1boxa3h4/ARxmESNyoCEa2gAKP/1BHqSXNMDPXIZNw6nDzCmj15XIda/m1djCE21K+b44BiMJhQ==", "dev": true, "dependencies": { - "chalk": "^4.1.2", - "commander": "^7.2.0", - "fs-extra": "^10.1.0", - "hap-nodejs": "~0.11.1", - "qrcode-terminal": "^0.12.0", - "semver": "^7.5.4", - "source-map-support": "^0.5.21" + "chalk": "4.1.2", + "commander": "12.0.0", + "fs-extra": "11.2.0", + "hap-nodejs": "0.12.0", + "qrcode-terminal": "0.12.0", + "semver": "7.6.0", + "source-map-support": "0.5.21" }, "bin": { "homebridge": "bin/homebridge" @@ -5385,29 +5361,6 @@ "node": "^18 || ^20" } }, - "node_modules/homebridge-config-ui-x/node_modules/commander": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", - "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/homebridge-config-ui-x/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/homebridge-lib": { "version": "6.7.5", "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", @@ -6654,9 +6607,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "devOptional": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -10293,9 +10246,9 @@ } }, "node_modules/typescript": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", - "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10324,9 +10277,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.12.0.tgz", - "integrity": "sha512-d87yk8lqSFUYtR5fTFe2frpkMIrUEz+lgoJmhcL+J3StVl+8fj8ytE4lLnJOTPCE12YbumNGzf4LYsQyusdV5g==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", + "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==", "engines": { "node": ">=18.0" } @@ -10880,9 +10833,9 @@ } }, "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "dev": true, "dependencies": { "sax": ">=0.6.0", diff --git a/package.json b/package.json index 10660a2c..60842c89 100644 --- a/package.json +++ b/package.json @@ -81,22 +81,22 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.12.0" + "undici": "^6.13.0" }, "optionalDependencies": { "node-switchbot": "2.1.0-beta.2" }, "devDependencies": { - "@types/node": "^20.12.6", - "@typescript-eslint/eslint-plugin": "^7.6.0", - "@typescript-eslint/parser": "^7.6.0", + "@types/node": "^20.12.7", + "@typescript-eslint/eslint-plugin": "^7.7.0", + "@typescript-eslint/parser": "^7.7.0", "eslint": "^8.57.0", - "homebridge": "^1.7.0", + "homebridge": "^1.8.0", "homebridge-config-ui-x": "4.56.1", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.18", "rimraf": "^5.0.5", "ts-node": "^10.9.2", - "typescript": "^5.4.4" + "typescript": "^5.4.5" } } diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index d9c74334..e14e9071 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -677,6 +677,8 @@ export class BlindTilt { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `Target Position: ${this.TargetPosition} sent over BLE, sent successfully`); }) .catch(async (e: any) => { this.apiError(e); @@ -759,6 +761,8 @@ export class BlindTilt { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1152,39 +1156,39 @@ export class BlindTilt { break; case 400: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); + + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); break; case 401: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); + + `but the request has not been authenticated, statusCode: ${statusCode}`); break; case 403: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); + + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); break; case 404: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); + + `statusCode: ${statusCode}`); break; case 406: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); + + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); break; case 415: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); + + `header that is not supported by the server, statusCode: ${statusCode}`); break; case 422: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); + + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`); break; case 429: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); + + `requests allowed for a given time window, statusCode: ${statusCode}`); break; case 500: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); + + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); break; default: this.infoLog( @@ -1437,6 +1441,12 @@ export class BlindTilt { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/bot.ts b/src/device/bot.ts index 7b581601..603bcc94 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -702,6 +702,8 @@ export class Bot { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.accessory.context.On = this.On; setTimeout(() => { if (this.botDeviceType === 'switch') { @@ -802,6 +804,8 @@ export class Bot { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1532,6 +1536,12 @@ export class Bot { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index a720d1d3..f7c31e1f 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -555,6 +555,8 @@ export class CeilingLight { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.On = false; }) .catch(async (e: any) => { @@ -601,6 +603,8 @@ export class CeilingLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -660,6 +664,8 @@ export class CeilingLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -746,6 +752,8 @@ export class CeilingLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1189,6 +1197,12 @@ export class CeilingLight { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index fce61c14..6991c632 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -621,6 +621,8 @@ export class ColorBulb { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.On = false; }) .catch(async (e: any) => { @@ -803,6 +805,8 @@ export class ColorBulb { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -862,6 +866,8 @@ export class ColorBulb { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -906,6 +912,8 @@ export class ColorBulb { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -948,6 +956,8 @@ export class ColorBulb { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1396,6 +1406,12 @@ export class ColorBulb { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/contact.ts b/src/device/contact.ts index 0c1fba58..99e200a8 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -755,6 +755,12 @@ export class Contact { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 94c04fe6..278f1f07 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -712,6 +712,8 @@ export class Curtain { }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `TargetPosition: ${this.TargetPosition} sent over BLE, sent successfully`); } catch (e) { this.apiError(e); this.errorLog( @@ -804,6 +806,8 @@ export class Curtain { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1359,6 +1363,12 @@ export class Curtain { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/hub.ts b/src/device/hub.ts index 3ebded2c..c23cfec1 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -786,6 +786,12 @@ export class Hub { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index b11d1fe2..ffa9afed 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -500,11 +500,13 @@ export class Humidifier { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target Position: ${this.Active}`); + this.infoLog(`${this.accessory.displayName} Active: ${this.Active}`); return await device_list[0].percentage(this.RelativeHumidityHumidifierThreshold); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `Active: ${this.Active} sent over BLE, sent successfully`); }) .catch(async (e: any) => { this.apiError(e); @@ -543,6 +545,8 @@ export class Humidifier { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -594,6 +598,8 @@ export class Humidifier { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -640,6 +646,8 @@ export class Humidifier { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1065,6 +1073,12 @@ export class Humidifier { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + async infoLog(...log: any[]): Promise { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index b987a701..786d6ab8 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -793,6 +793,12 @@ export class IOSensor { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 01dae57b..03e8d6fd 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -533,6 +533,8 @@ export class StripLight { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.On = false; }) .catch(async (e: any) => { @@ -672,6 +674,8 @@ export class StripLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -728,6 +732,8 @@ export class StripLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -770,6 +776,8 @@ export class StripLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -1217,6 +1225,12 @@ export class StripLight { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/lock.ts b/src/device/lock.ts index 2dc1244a..0ee7c2dd 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -546,6 +546,8 @@ export class Lock { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `LockTargetState: ${this.LockTargetState} sent over BLE, sent successfully`); this.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; }) .catch(async (e: any) => { @@ -596,6 +598,8 @@ export class Lock { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -890,6 +894,12 @@ export class Lock { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/meter.ts b/src/device/meter.ts index 4677e55f..5ff470c6 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -786,6 +786,12 @@ export class Meter { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 49ae0309..abd527e0 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -801,6 +801,12 @@ export class MeterPlus { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/motion.ts b/src/device/motion.ts index 18d495b2..1e12cb29 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -715,6 +715,12 @@ export class Motion { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/plug.ts b/src/device/plug.ts index e3e9e9ae..c9d77c71 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -386,6 +386,8 @@ export class Plug { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.On = false; }) .catch(async (e: any) => { @@ -432,6 +434,8 @@ export class Plug { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -740,6 +744,12 @@ export class Plug { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 056cfa19..67523693 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -434,6 +434,8 @@ export class RobotVacuumCleaner { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `On: ${this.On} sent over BLE, sent successfully`); this.On = false; }) .catch(async (e: any) => { @@ -480,6 +482,8 @@ export class RobotVacuumCleaner { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${bodyChange} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -502,10 +506,11 @@ export class RobotVacuumCleaner { async openAPIpushBrightnessChanges() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushBrightnessChanges`); - const body = await this.brightnessCommands(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${body},`); + const bodyChange = this.brightnessCommands(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); try { const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { + body: bodyChange, method: 'POST', headers: this.platform.generateHeaders(), }); @@ -516,6 +521,8 @@ export class RobotVacuumCleaner { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -566,12 +573,12 @@ export class RobotVacuumCleaner { command = 'dock'; parameter = 'default'; } - const body = JSON.stringify({ + const bodyChange = JSON.stringify({ command: `${command}`, parameter: `${parameter}`, commandType: 'command', }); - return body; + return bodyChange; } /** @@ -898,6 +905,12 @@ export class RobotVacuumCleaner { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/device/waterdetector.json b/src/device/waterdetector.json deleted file mode 100644 index f24f250b..00000000 --- a/src/device/waterdetector.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "statusCode": 100, - "body": { - "deviceId": "XXXXXXXXXXXXXX", - "hubDeviceId": "XXXXXXXXXXXXXX", - "status": 0, - "battery": 100, - "version": "V1.3-1.3" - }, - "message": "success" -} \ No newline at end of file diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index cab2d5bc..89449a95 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -639,6 +639,12 @@ export class WaterDetector { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index e97e879e..f254605a 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -275,6 +275,7 @@ export class AirConditioner { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -799,6 +800,12 @@ export class AirConditioner { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 6669aa94..62283a24 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -227,6 +227,7 @@ export class AirPurifier { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -478,6 +479,12 @@ export class AirPurifier { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 0283077e..0a781c69 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -132,6 +132,7 @@ export class Camera { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -354,6 +355,12 @@ export class Camera { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 8f9c80eb..83eba172 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -249,6 +249,7 @@ export class Fan { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -486,6 +487,12 @@ export class Fan { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index ebefa21f..6dd023d1 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -247,6 +247,7 @@ export class Light { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.accessory.context.On = this.On; this.updateHomeKitCharacteristics(); } else { @@ -502,6 +503,12 @@ export class Light { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 467f95be..61cb4b5f 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -439,6 +439,7 @@ export class Others { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -975,6 +976,12 @@ export class Others { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 9a58fb4e..7c16f2e7 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -381,6 +381,7 @@ export class TV { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -628,6 +629,12 @@ export class TV { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 9b0e58e2..64e6c30c 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -128,6 +128,7 @@ export class VacuumCleaner { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -350,6 +351,12 @@ export class VacuumCleaner { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index 0edd3e7e..9a262951 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -137,6 +137,7 @@ export class WaterHeater { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -359,6 +360,12 @@ export class WaterHeater { /** * Logging for Device */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.info(String(...log)); diff --git a/src/platform.ts b/src/platform.ts index fac224b6..6da23267 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -2688,6 +2688,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { * If device level logging is turned on, log to log.warn * Otherwise send debug logs to log.debug */ + successLog(...log: any[]): void { + if (this.enablingPlatformLogging()) { + this.log.success(String(...log)); + } + } + infoLog(...log: any[]): void { if (this.enablingPlatformLogging()) { this.log.info(String(...log)); From 0c76e8db2ecfbeecddd2ebdd4fd66310c348f9d5 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:31:41 -0500 Subject: [PATCH 12/73] Update package.json --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 60842c89..e222260e 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,12 @@ "url": "https://github.com/OpenWonderLabs/homebridge-switchbot/issues" }, "engines": { - "homebridge": "^1.7.0", + "homebridge": "^1.8.0", "node": "^18 || ^20" }, + "engine-strict": { + "homebridge": "^1.8.0" + }, "main": "dist/index.js", "scripts": { "check": "npm install && npm outdated", From 5a5aa17169258f11a8e3d062365c0f845d240a56 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:34:51 -0500 Subject: [PATCH 13/73] Update package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index e222260e..f0ddd68d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "url": "https://github.com/OpenWonderLabs/homebridge-switchbot/issues" }, "engines": { - "homebridge": "^1.8.0", "node": "^18 || ^20" }, "engine-strict": { From e55a9656cb1905873d8abab8c8fbf04e2feed03b Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:35:43 -0500 Subject: [PATCH 14/73] Update package.json --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f0ddd68d..4f1f2ebb 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,10 @@ "url": "https://github.com/OpenWonderLabs/homebridge-switchbot/issues" }, "engines": { + "homebridge": "^1.8.0", "node": "^18 || ^20" }, - "engine-strict": { - "homebridge": "^1.8.0" - }, + "engine-strict": true, "main": "dist/index.js", "scripts": { "check": "npm install && npm outdated", From 6e5f51f406fa4c21f4fb9c6506ab9dc936ded146 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:40:41 -0500 Subject: [PATCH 15/73] try another thing --- .npmrc | 2 ++ package.json | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..e84e4e8c --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +# .npmrc +engine-strict=true \ No newline at end of file diff --git a/package.json b/package.json index 4f1f2ebb..a8a4c612 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "homebridge": "^1.8.0", "node": "^18 || ^20" }, - "engine-strict": true, "main": "dist/index.js", "scripts": { "check": "npm install && npm outdated", From 1cd1e1a8c19f207c00e5d1ea0ab7e740d0dbcac3 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Apr 2024 20:43:25 -0500 Subject: [PATCH 16/73] engineStrict --- .npmrc | 2 -- package.json | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .npmrc diff --git a/.npmrc b/.npmrc deleted file mode 100644 index e84e4e8c..00000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -# .npmrc -engine-strict=true \ No newline at end of file diff --git a/package.json b/package.json index a8a4c612..76bb6655 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bugs": { "url": "https://github.com/OpenWonderLabs/homebridge-switchbot/issues" }, + "engineStrict": true, "engines": { "homebridge": "^1.8.0", "node": "^18 || ^20" From 84d6605528ab8c9958d40ceaebf9fb652c7975d1 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 24 Apr 2024 22:24:16 -0500 Subject: [PATCH 17/73] update dependenices --- package-lock.json | 116 ++++---- package.json | 8 +- src/custom.d.ts | 2 +- src/device/ceilinglight.ts | 4 +- src/device/colorbulb.ts | 4 +- src/device/lightstrip.ts | 4 +- src/index.ts | 2 +- src/platform.ts | 2 +- src/settings.ts | 556 +----------------------------------- src/utils.ts | 557 ++++++++++++++++++++++++++++++++++++- 10 files changed, 628 insertions(+), 627 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ee27735..5ef7803f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,12 +24,12 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.13.0" + "undici": "^6.14.1" }, "devDependencies": { "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.7.0", - "@typescript-eslint/parser": "^7.7.0", + "@typescript-eslint/eslint-plugin": "^7.7.1", + "@typescript-eslint/parser": "^7.7.1", "eslint": "^8.57.0", "homebridge": "^1.8.0", "homebridge-config-ui-x": "4.56.1", @@ -40,11 +40,11 @@ "typescript": "^5.4.5" }, "engines": { - "homebridge": "^1.7.0", + "homebridge": "^1.8.0", "node": "^18 || ^20" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.2" + "node-switchbot": "2.1.0-beta.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -57,9 +57,9 @@ } }, "node_modules/@abandonware/bluetooth-hci-socket": { - "version": "0.5.3-11", - "resolved": "https://registry.npmjs.org/@abandonware/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.3-11.tgz", - "integrity": "sha512-gOFnpLtSzYFSsNgR609TdFUGVvX7dhc8nrarcfmWZNgtlzRS0IJq7/xxpTw/G4SicEkNlT6lDi6eHwurIT+axg==", + "version": "0.5.3-12", + "resolved": "https://registry.npmjs.org/@abandonware/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.3-12.tgz", + "integrity": "sha512-qo2cBoh94j6RPusaNXSLYI8Bzxuz01Bx3MD80a/QYzhHED/FZ6Y0k2w2kRbfIA2EEhFSCbXrBZDQlpilL4nbxA==", "hasInstallScript": true, "optional": true, "os": [ @@ -2147,16 +2147,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz", - "integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz", + "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.7.0", - "@typescript-eslint/type-utils": "7.7.0", - "@typescript-eslint/utils": "7.7.0", - "@typescript-eslint/visitor-keys": "7.7.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/type-utils": "7.7.1", + "@typescript-eslint/utils": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.3.1", @@ -2182,15 +2182,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz", - "integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz", + "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.7.0", - "@typescript-eslint/types": "7.7.0", - "@typescript-eslint/typescript-estree": "7.7.0", - "@typescript-eslint/visitor-keys": "7.7.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/typescript-estree": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4" }, "engines": { @@ -2210,13 +2210,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz", - "integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", + "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.0", - "@typescript-eslint/visitor-keys": "7.7.0" + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2227,13 +2227,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz", - "integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz", + "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.7.0", - "@typescript-eslint/utils": "7.7.0", + "@typescript-eslint/typescript-estree": "7.7.1", + "@typescript-eslint/utils": "7.7.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2254,9 +2254,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz", - "integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", + "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2267,13 +2267,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz", - "integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", + "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.0", - "@typescript-eslint/visitor-keys": "7.7.0", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2295,17 +2295,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz", - "integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz", + "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.15", "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.7.0", - "@typescript-eslint/types": "7.7.0", - "@typescript-eslint/typescript-estree": "7.7.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/typescript-estree": "7.7.1", "semver": "^7.6.0" }, "engines": { @@ -2320,12 +2320,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz", - "integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", + "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/types": "7.7.1", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7067,15 +7067,15 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.0-beta.2", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.2.tgz", - "integrity": "sha512-YIlEhBTfLCIUz4/HAH9hguQCcXevCv0dpnK+GzPFggoftHPV7Spk3L9IY8oUuTrCkU7VVtpn+l8MolumHep4cQ==", + "version": "2.1.0-beta.4", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.4.tgz", + "integrity": "sha512-nzqiHeNB9EyJ+2aDsfkjuiU8/OgHO6NgNU9g8qgP/qF0k3TYXb+qK6EKyd9lyWZlEy6oG80xdaU9PH0I095KpQ==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-24" }, "optionalDependencies": { - "@abandonware/bluetooth-hci-socket": "^0.5.3-11" + "@abandonware/bluetooth-hci-socket": "^0.5.3-12" } }, "node_modules/nodemon": { @@ -10277,11 +10277,11 @@ "dev": true }, "node_modules/undici": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", - "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.14.1.tgz", + "integrity": "sha512-mAel3i4BsYhkeVPXeIPXVGPJKeBzqCieZYoFsbWfUzd68JmHByhc1Plit5WlylxXFaGpgkZB8mExlxnt+Q1p7A==", "engines": { - "node": ">=18.0" + "node": ">=18.17" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 76bb6655..14989cf3 100644 --- a/package.json +++ b/package.json @@ -82,15 +82,15 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", - "undici": "^6.13.0" + "undici": "^6.14.1" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.2" + "node-switchbot": "2.1.0-beta.4" }, "devDependencies": { "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.7.0", - "@typescript-eslint/parser": "^7.7.0", + "@typescript-eslint/eslint-plugin": "^7.7.1", + "@typescript-eslint/parser": "^7.7.1", "eslint": "^8.57.0", "homebridge": "^1.8.0", "homebridge-config-ui-x": "4.56.1", diff --git a/src/custom.d.ts b/src/custom.d.ts index eea79da3..ebb1e5af 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -1,4 +1,4 @@ -/* Copyright(C) 2017-2023, donavanbecker (https://github.com/donavanbecker). All rights reserved. +/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * custom.d.ts: @switchbot/homebridge-switchbot platform class. */ diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index f7c31e1f..edafaf9a 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -1,9 +1,9 @@ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, hs2rgb, rgb2hs, m2hs, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, } from 'homebridge'; diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 6991c632..5b2b39bc 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -1,9 +1,9 @@ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, hs2rgb, rgb2hs, m2hs, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, } from 'homebridge'; diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 03e8d6fd..f0416b0e 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -1,12 +1,12 @@ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, } from 'homebridge'; -import { device, devicesConfig, hs2rgb, rgb2hs, deviceStatus, serviceData, m2hs, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; /** * Platform Accessory diff --git a/src/index.ts b/src/index.ts index f3540885..d7d636b6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -/* Copyright(C) 2021-2023, SwitchBot (https://github.com/SwitchBot). All rights reserved. +/* Copyright(C) 2021-2024, SwitchBot (https://github.com/SwitchBot). All rights reserved. * * index.ts: @switchbot/homebridge-switchbot plugin registration. */ diff --git a/src/platform.ts b/src/platform.ts index 6da23267..043dbe78 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,4 +1,4 @@ -/* Copyright(C) 2017-2023, donavanbecker (https://github.com/donavanbecker). All rights reserved. +/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * platform.ts: @switchbot/homebridge-switchbot platform class. */ diff --git a/src/settings.ts b/src/settings.ts index e3fba114..cb66398b 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,4 +1,4 @@ -/* Copyright(C) 2017-2023, donavanbecker (https://github.com/donavanbecker). All rights reserved. +/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * settings.ts: @switchbot/homebridge-switchbot platform class. */ @@ -406,557 +406,3 @@ export type switchbot = { wait: (arg0: number) => any; }; -export function rgb2hs(r: any, g: any, b: any) { - /* - Credit: - https://github.com/WickyNilliams/pure-color - */ - r = parseInt(r); - g = parseInt(g); - b = parseInt(b); - const min = Math.min(r, g, b); - const max = Math.max(r, g, b); - const delta = max - min; - let h; - let s; - if (max === 0) { - s = 0; - } else { - s = (delta / max) * 100; - } - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } - h = Math.min(h * 60, 360); - - if (h < 0) { - h += 360; - } - return [Math.round(h), Math.round(s)]; -} - -export function hs2rgb(h: any, s: any) { - /* - Credit: - https://github.com/WickyNilliams/pure-color - */ - h = parseInt(h) / 60; - s = parseInt(s) / 100; - const f = h - Math.floor(h); - const p = 255 * (1 - s); - const q = 255 * (1 - s * f); - const t = 255 * (1 - s * (1 - f)); - let rgb; - switch (Math.floor(h) % 6) { - case 0: - rgb = [255, t, p]; - break; - case 1: - rgb = [q, 255, p]; - break; - case 2: - rgb = [p, 255, t]; - break; - case 3: - rgb = [p, q, 255]; - break; - case 4: - rgb = [t, p, 255]; - break; - case 5: - rgb = [255, p, q]; - break; - } - if (rgb[0] === 255) { - rgb[1] *= 0.8; - rgb[2] *= 0.8; - if (rgb[1] <= 25 && rgb[2] <= 25) { - rgb[1] = 0; - rgb[2] = 0; - } - } - return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]; -} - -export function k2rgb(k: number) { - // Set kelvin to nearest 100, between 2000 and 7100 - k = Math.round(k / 100) * 100; - k = Math.max(Math.min(k, 7100), 2000); - - // k should now appear in our table of kelvin to rgb - const values = { - 2000: [255, 141, 11], - 2100: [255, 146, 29], - 2200: [255, 147, 44], - 2300: [255, 152, 54], - 2400: [255, 157, 63], - 2500: [255, 166, 69], - 2600: [255, 170, 77], - 2700: [255, 174, 84], - 2800: [255, 173, 94], - 2900: [255, 177, 101], - 3000: [255, 180, 107], - 3100: [255, 189, 111], - 3200: [255, 187, 120], - 3300: [255, 195, 124], - 3400: [255, 198, 130], - 3500: [255, 201, 135], - 3600: [255, 203, 141], - 3700: [255, 206, 146], - 3800: [255, 204, 153], - 3900: [255, 206, 159], - 4000: [255, 213, 161], - 4100: [255, 215, 166], - 4200: [255, 217, 171], - 4300: [255, 219, 175], - 4400: [255, 221, 180], - 4500: [255, 223, 184], - 4600: [255, 225, 188], - 4700: [255, 226, 192], - 4800: [255, 228, 196], - 4900: [255, 229, 200], - 5000: [255, 231, 204], - 5100: [255, 230, 210], - 5200: [255, 234, 211], - 5300: [255, 235, 215], - 5400: [255, 237, 218], - 5500: [255, 236, 224], - 5700: [255, 240, 228], - 5800: [255, 241, 231], - 5900: [255, 243, 234], - 6000: [255, 244, 237], - 6100: [255, 245, 240], - 6200: [255, 246, 243], - 6300: [255, 247, 247], - 6400: [255, 248, 251], - 6500: [255, 249, 253], - 6600: [254, 249, 255], - 6700: [252, 247, 255], - 6800: [249, 246, 255], - 6900: [247, 245, 255], - 7000: [245, 243, 255], - 7100: [243, 242, 255], - }; - - // Return the value - return values[k]; -} - -export function m2hs(m) { - /* - Credit: - https://github.com/homebridge/HAP-NodeJS - */ - const table = { - 100: [19, 222.1], - 101: [18.7, 222.2], - 102: [18.4, 222.3], - 103: [18.2, 222.3], - 104: [17.9, 222.4], - 105: [17.6, 222.5], - 106: [17.3, 222.7], - 107: [17, 222.8], - 108: [16.7, 222.9], - 109: [16.4, 223], - 110: [16.1, 223.2], - 111: [15.8, 223.3], - 112: [15.4, 223.4], - 113: [15.2, 223.6], - 114: [14.9, 223.8], - 115: [14.7, 223.9], - 116: [14.3, 224.1], - 117: [14.1, 224.2], - 118: [13.8, 224.4], - 119: [13.5, 224.6], - 120: [13.2, 224.8], - 121: [12.9, 225], - 122: [12.5, 225.3], - 123: [12.2, 225.6], - 124: [11.8, 225.9], - 125: [11.4, 226.3], - 126: [11.1, 226.7], - 127: [10.7, 227.1], - 128: [10.3, 227.6], - 129: [9.9, 228], - 130: [9.6, 228.5], - 131: [9.3, 229.1], - 132: [8.9, 229.6], - 133: [8.5, 230.2], - 134: [8.2, 230.9], - 135: [7.8, 231.6], - 136: [7.5, 232.5], - 137: [7.1, 233.5], - 138: [6.7, 234.6], - 139: [6.3, 235.8], - 140: [6, 237.1], - 141: [5.6, 238.9], - 142: [5.2, 240.9], - 143: [5, 242.9], - 144: [4.8, 244.9], - 145: [4.6, 246.9], - 146: [4.4, 249.3], - 147: [4.3, 251.9], - 148: [4.1, 254.9], - 149: [3.9, 258], - 150: [3.7, 261.8], - 151: [3.4, 265.9], - 152: [3.2, 271], - 153: [3, 276.4], - 154: [2.8, 283.6], - 155: [2.6, 290.4], - 156: [2.3, 295.3], - 157: [2.1, 300], - 158: [1.9, 300], - 159: [1.6, 300], - 160: [1.4, 195.8], - 161: [1.2, 84.3], - 162: [1.3, 58.2], - 163: [1.5, 55.9], - 164: [1.7, 53.2], - 165: [1.9, 50.2], - 166: [2.1, 47.1], - 167: [2.4, 44.5], - 168: [2.6, 42.6], - 169: [2.9, 40.9], - 170: [3.1, 39.5], - 171: [3.4, 38.3], - 172: [3.7, 37.3], - 173: [3.9, 36.5], - 174: [4.2, 35.7], - 175: [4.4, 35.1], - 176: [4.6, 34.5], - 177: [4.9, 34], - 178: [5.1, 33.5], - 179: [5.3, 33], - 180: [5.6, 32.7], - 181: [5.8, 32.3], - 182: [6, 32], - 183: [6.3, 31.7], - 184: [6.5, 31.4], - 185: [6.7, 31.2], - 186: [7, 30.9], - 187: [7.2, 30.7], - 188: [7.4, 30.5], - 189: [7.6, 30.3], - 190: [7.9, 30.1], - 191: [8.1, 29.9], - 192: [8.4, 29.7], - 193: [8.6, 29.6], - 194: [8.9, 29.5], - 195: [9.1, 29.3], - 196: [9.4, 29.2], - 197: [9.6, 29.1], - 198: [9.8, 29], - 199: [10, 28.9], - 200: [10.2, 28.7], - 201: [10.5, 28.7], - 202: [10.7, 28.6], - 203: [11, 28.5], - 204: [11.2, 28.4], - 205: [11.4, 28.3], - 206: [11.6, 28.3], - 207: [11.8, 28.2], - 208: [12.1, 28.1], - 209: [12.3, 28.1], - 210: [12.5, 28], - 211: [12.7, 28], - 212: [12.9, 27.9], - 213: [13.2, 27.8], - 214: [13.4, 27.8], - 215: [13.6, 27.7], - 216: [13.8, 27.7], - 217: [14, 27.7], - 218: [14.3, 27.6], - 219: [14.5, 27.6], - 220: [14.7, 27.5], - 221: [14.9, 27.5], - 222: [15.1, 27.5], - 223: [15.3, 27.4], - 224: [15.5, 27.4], - 225: [15.8, 27.4], - 226: [16, 27.3], - 227: [16.2, 27.3], - 228: [16.4, 27.3], - 229: [16.6, 27.3], - 230: [16.8, 27.2], - 231: [17, 27.2], - 232: [17.2, 27.2], - 233: [17.4, 27.2], - 234: [17.6, 27.2], - 235: [17.8, 27.1], - 236: [18, 27.1], - 237: [18.2, 27.1], - 238: [18.4, 27.1], - 239: [18.7, 27.1], - 240: [18.8, 27], - 241: [19, 27], - 242: [19.2, 27], - 243: [19.4, 27], - 244: [19.6, 27], - 245: [19.8, 27], - 246: [20, 27], - 247: [20.3, 26.9], - 248: [20.5, 26.9], - 249: [20.6, 26.9], - 250: [20.8, 26.9], - 251: [21, 26.9], - 252: [21.3, 26.9], - 253: [21.5, 26.9], - 254: [21.6, 26.9], - 255: [21.8, 26.8], - 256: [22, 26.8], - 257: [22.2, 26.8], - 258: [22.4, 26.8], - 259: [22.6, 26.8], - 260: [22.8, 26.8], - 261: [23, 26.8], - 262: [23.2, 26.8], - 263: [23.4, 26.8], - 264: [23.6, 26.8], - 265: [23.8, 26.8], - 266: [24, 26.8], - 267: [24.1, 26.8], - 268: [24.3, 26.8], - 269: [24.5, 26.8], - 270: [24.7, 26.8], - 271: [24.8, 26.8], - 272: [25.1, 26.7], - 273: [25.3, 26.7], - 274: [25.4, 26.7], - 275: [25.6, 26.7], - 276: [25.8, 26.7], - 277: [26, 26.7], - 278: [26.1, 26.7], - 279: [26.3, 26.7], - 280: [26.5, 26.7], - 281: [26.7, 26.7], - 282: [26.9, 26.7], - 283: [27.1, 26.7], - 284: [27.3, 26.7], - 285: [27.5, 26.7], - 286: [27.7, 26.7], - 287: [27.8, 26.7], - 288: [28, 26.7], - 289: [28.2, 26.7], - 290: [28.4, 26.7], - 291: [28.6, 26.7], - 292: [28.8, 26.7], - 293: [28.9, 26.7], - 294: [29.1, 26.7], - 295: [29.3, 26.7], - 296: [29.5, 26.7], - 297: [29.6, 26.7], - 298: [29.8, 26.7], - 299: [30, 26.7], - 300: [30.2, 26.7], - 301: [30.4, 26.7], - 302: [30.5, 26.7], - 303: [30.7, 26.7], - 304: [30.9, 26.7], - 305: [31.1, 26.7], - 306: [31.2, 26.7], - 307: [31.4, 26.7], - 308: [31.6, 26.7], - 309: [31.8, 26.8], - 310: [31.9, 26.8], - 311: [32.1, 26.8], - 312: [32.3, 26.8], - 313: [32.5, 26.8], - 314: [32.6, 26.8], - 315: [32.8, 26.8], - 316: [33, 26.8], - 317: [33.2, 26.8], - 318: [33.3, 26.8], - 319: [33.5, 26.8], - 320: [33.7, 26.8], - 321: [33.8, 26.8], - 322: [34, 26.8], - 323: [34.2, 26.8], - 324: [34.4, 26.8], - 325: [34.5, 26.8], - 326: [34.7, 26.8], - 327: [34.9, 26.8], - 328: [35.1, 26.8], - 329: [35.2, 26.8], - 330: [35.4, 26.8], - 331: [35.5, 26.8], - 332: [35.7, 26.8], - 333: [35.9, 26.8], - 334: [36.1, 26.8], - 335: [36.3, 26.9], - 336: [36.5, 26.9], - 337: [36.7, 26.9], - 338: [36.9, 26.9], - 339: [37.1, 26.9], - 340: [37.2, 26.9], - 341: [37.4, 26.9], - 342: [37.5, 26.9], - 343: [37.7, 26.9], - 344: [37.9, 26.9], - 345: [38.1, 26.9], - 346: [38.3, 26.9], - 347: [38.5, 26.9], - 348: [38.7, 26.9], - 349: [38.9, 26.9], - 350: [39, 26.9], - 351: [39.2, 26.9], - 352: [39.3, 27], - 353: [39.5, 27], - 354: [39.7, 27], - 355: [39.9, 27], - 356: [40.1, 27], - 357: [40.2, 27], - 358: [40.4, 27], - 359: [40.6, 27], - 360: [40.8, 27], - 361: [40.9, 27], - 362: [41.1, 27], - 363: [41.2, 27], - 364: [41.4, 27], - 365: [41.6, 27], - 366: [41.8, 27], - 367: [42, 27], - 368: [42.1, 27.1], - 369: [42.3, 27.1], - 370: [42.4, 27.1], - 371: [42.6, 27.1], - 372: [42.8, 27.1], - 373: [43, 27.1], - 374: [43.1, 27.1], - 375: [43.2, 27.1], - 376: [43.4, 27.1], - 377: [43.6, 27.1], - 378: [43.8, 27.1], - 379: [43.9, 27.1], - 380: [44.1, 27.1], - 381: [44.3, 27.2], - 382: [44.4, 27.2], - 383: [44.6, 27.2], - 384: [44.7, 27.2], - 385: [44.9, 27.2], - 386: [45.1, 27.2], - 387: [45.3, 27.2], - 388: [45.5, 27.2], - 389: [45.6, 27.2], - 390: [45.8, 27.2], - 391: [46, 27.2], - 392: [46.2, 27.2], - 393: [46.4, 27.3], - 394: [46.5, 27.3], - 395: [46.7, 27.3], - 396: [46.9, 27.3], - 397: [47.1, 27.3], - 398: [47.2, 27.3], - 399: [47.4, 27.3], - 400: [47.6, 27.3], - 401: [47.7, 27.3], - 402: [47.9, 27.3], - 403: [48.1, 27.3], - 404: [48.3, 27.3], - 405: [48.5, 27.4], - 406: [48.7, 27.4], - 407: [48.8, 27.4], - 408: [49, 27.4], - 409: [49.2, 27.4], - 410: [49.4, 27.4], - 411: [49.6, 27.4], - 412: [49.7, 27.4], - 413: [49.9, 27.4], - 414: [50.1, 27.4], - 415: [50.2, 27.4], - 416: [50.4, 27.4], - 417: [50.6, 27.5], - 418: [50.7, 27.5], - 419: [50.9, 27.5], - 420: [51.1, 27.5], - 421: [51.2, 27.5], - 422: [51.4, 27.5], - 423: [51.6, 27.5], - 424: [51.7, 27.5], - 425: [51.9, 27.5], - 426: [52.1, 27.5], - 427: [51.2, 27.6], - 428: [52.4, 27.6], - 429: [52.5, 27.6], - 430: [52.7, 27.6], - 431: [52.9, 27.6], - 432: [53.1, 27.6], - 433: [53.2, 27.6], - 434: [53.4, 27.6], - 435: [53.6, 27.6], - 436: [53.7, 27.6], - 437: [53.9, 27.6], - 438: [54.1, 27.7], - 439: [54.2, 27.7], - 440: [54.3, 27.7], - 441: [54.5, 27.7], - 442: [54.7, 27.7], - 443: [54.8, 27.7], - 444: [55, 27.7], - 445: [55.2, 27.7], - 446: [55.3, 27.7], - 447: [55.5, 27.7], - 448: [55.7, 27.7], - 449: [55.8, 27.8], - 450: [56, 27.8], - 451: [56.2, 27.8], - 452: [56.3, 27.8], - 453: [56.5, 27.8], - 454: [56.7, 27.8], - 455: [56.8, 27.8], - 456: [57, 27.8], - 457: [57.2, 27.8], - 458: [57.3, 27.9], - 459: [57.4, 27.9], - 460: [57.6, 27.9], - 461: [57.8, 27.9], - 462: [57.9, 27.9], - 463: [58.1, 27.9], - 464: [58.3, 27.9], - 465: [58.4, 27.9], - 466: [58.6, 27.9], - 467: [58.8, 27.9], - 468: [59, 28], - 469: [59.1, 28], - 470: [59.2, 28], - 471: [59.4, 28], - 472: [59.6, 28], - 473: [59.7, 28], - 474: [60, 28], - 475: [60.1, 28], - 476: [60.2, 28], - 477: [60.4, 28], - 478: [60.6, 28.1], - 479: [60.7, 28.1], - 480: [60.9, 28.1], - 481: [60.1, 28.1], - 482: [60.3, 28.1], - 483: [61.4, 28.1], - 484: [61.5, 28.1], - 485: [61.7, 28.1], - 486: [61.9, 28.1], - 487: [62, 28.2], - 488: [62.2, 28.2], - 489: [62.3, 28.2], - 490: [62.5, 28.2], - 491: [62.7, 28.2], - 492: [62.8, 28.2], - 493: [63, 28.2], - 494: [63.2, 28.2], - 495: [63.3, 28.2], - 496: [63.4, 28.2], - 497: [63.6, 28.2], - 498: [63.8, 28.3], - 499: [63.9, 28.3], - 500: [64.1, 28.3], - }; - const input = Math.min(Math.max(Math.round(m), 140), 500); - const toReturn = table[input]; - return [Math.round(toReturn[1]), Math.round(toReturn[0])]; -} diff --git a/src/utils.ts b/src/utils.ts index 1543c916..597a06c9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,562 @@ -/* Copyright(C) 2017-2023, donavanbecker (https://github.com/donavanbecker). All rights reserved. +/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * util.ts: @switchbot/homebridge-switchbot platform class. */ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } + +export function rgb2hs(r: any, g: any, b: any) { + /* + Credit: + https://github.com/WickyNilliams/pure-color + */ + r = parseInt(r); + g = parseInt(g); + b = parseInt(b); + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; + if (max === 0) { + s = 0; + } else { + s = (delta / max) * 100; + } + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + return [Math.round(h), Math.round(s)]; +} + +export function hs2rgb(h: any, s: any) { + /* + Credit: + https://github.com/WickyNilliams/pure-color + */ + h = parseInt(h) / 60; + s = parseInt(s) / 100; + const f = h - Math.floor(h); + const p = 255 * (1 - s); + const q = 255 * (1 - s * f); + const t = 255 * (1 - s * (1 - f)); + let rgb; + switch (Math.floor(h) % 6) { + case 0: + rgb = [255, t, p]; + break; + case 1: + rgb = [q, 255, p]; + break; + case 2: + rgb = [p, 255, t]; + break; + case 3: + rgb = [p, q, 255]; + break; + case 4: + rgb = [t, p, 255]; + break; + case 5: + rgb = [255, p, q]; + break; + } + if (rgb[0] === 255) { + rgb[1] *= 0.8; + rgb[2] *= 0.8; + if (rgb[1] <= 25 && rgb[2] <= 25) { + rgb[1] = 0; + rgb[2] = 0; + } + } + return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]; +} + +export function k2rgb(k: number) { + // Set kelvin to nearest 100, between 2000 and 7100 + k = Math.round(k / 100) * 100; + k = Math.max(Math.min(k, 7100), 2000); + + // k should now appear in our table of kelvin to rgb + const values = { + 2000: [255, 141, 11], + 2100: [255, 146, 29], + 2200: [255, 147, 44], + 2300: [255, 152, 54], + 2400: [255, 157, 63], + 2500: [255, 166, 69], + 2600: [255, 170, 77], + 2700: [255, 174, 84], + 2800: [255, 173, 94], + 2900: [255, 177, 101], + 3000: [255, 180, 107], + 3100: [255, 189, 111], + 3200: [255, 187, 120], + 3300: [255, 195, 124], + 3400: [255, 198, 130], + 3500: [255, 201, 135], + 3600: [255, 203, 141], + 3700: [255, 206, 146], + 3800: [255, 204, 153], + 3900: [255, 206, 159], + 4000: [255, 213, 161], + 4100: [255, 215, 166], + 4200: [255, 217, 171], + 4300: [255, 219, 175], + 4400: [255, 221, 180], + 4500: [255, 223, 184], + 4600: [255, 225, 188], + 4700: [255, 226, 192], + 4800: [255, 228, 196], + 4900: [255, 229, 200], + 5000: [255, 231, 204], + 5100: [255, 230, 210], + 5200: [255, 234, 211], + 5300: [255, 235, 215], + 5400: [255, 237, 218], + 5500: [255, 236, 224], + 5700: [255, 240, 228], + 5800: [255, 241, 231], + 5900: [255, 243, 234], + 6000: [255, 244, 237], + 6100: [255, 245, 240], + 6200: [255, 246, 243], + 6300: [255, 247, 247], + 6400: [255, 248, 251], + 6500: [255, 249, 253], + 6600: [254, 249, 255], + 6700: [252, 247, 255], + 6800: [249, 246, 255], + 6900: [247, 245, 255], + 7000: [245, 243, 255], + 7100: [243, 242, 255], + }; + + // Return the value + return values[k]; +} + +export function m2hs(m) { + /* + Credit: + https://github.com/homebridge/HAP-NodeJS + */ + const table = { + 100: [19, 222.1], + 101: [18.7, 222.2], + 102: [18.4, 222.3], + 103: [18.2, 222.3], + 104: [17.9, 222.4], + 105: [17.6, 222.5], + 106: [17.3, 222.7], + 107: [17, 222.8], + 108: [16.7, 222.9], + 109: [16.4, 223], + 110: [16.1, 223.2], + 111: [15.8, 223.3], + 112: [15.4, 223.4], + 113: [15.2, 223.6], + 114: [14.9, 223.8], + 115: [14.7, 223.9], + 116: [14.3, 224.1], + 117: [14.1, 224.2], + 118: [13.8, 224.4], + 119: [13.5, 224.6], + 120: [13.2, 224.8], + 121: [12.9, 225], + 122: [12.5, 225.3], + 123: [12.2, 225.6], + 124: [11.8, 225.9], + 125: [11.4, 226.3], + 126: [11.1, 226.7], + 127: [10.7, 227.1], + 128: [10.3, 227.6], + 129: [9.9, 228], + 130: [9.6, 228.5], + 131: [9.3, 229.1], + 132: [8.9, 229.6], + 133: [8.5, 230.2], + 134: [8.2, 230.9], + 135: [7.8, 231.6], + 136: [7.5, 232.5], + 137: [7.1, 233.5], + 138: [6.7, 234.6], + 139: [6.3, 235.8], + 140: [6, 237.1], + 141: [5.6, 238.9], + 142: [5.2, 240.9], + 143: [5, 242.9], + 144: [4.8, 244.9], + 145: [4.6, 246.9], + 146: [4.4, 249.3], + 147: [4.3, 251.9], + 148: [4.1, 254.9], + 149: [3.9, 258], + 150: [3.7, 261.8], + 151: [3.4, 265.9], + 152: [3.2, 271], + 153: [3, 276.4], + 154: [2.8, 283.6], + 155: [2.6, 290.4], + 156: [2.3, 295.3], + 157: [2.1, 300], + 158: [1.9, 300], + 159: [1.6, 300], + 160: [1.4, 195.8], + 161: [1.2, 84.3], + 162: [1.3, 58.2], + 163: [1.5, 55.9], + 164: [1.7, 53.2], + 165: [1.9, 50.2], + 166: [2.1, 47.1], + 167: [2.4, 44.5], + 168: [2.6, 42.6], + 169: [2.9, 40.9], + 170: [3.1, 39.5], + 171: [3.4, 38.3], + 172: [3.7, 37.3], + 173: [3.9, 36.5], + 174: [4.2, 35.7], + 175: [4.4, 35.1], + 176: [4.6, 34.5], + 177: [4.9, 34], + 178: [5.1, 33.5], + 179: [5.3, 33], + 180: [5.6, 32.7], + 181: [5.8, 32.3], + 182: [6, 32], + 183: [6.3, 31.7], + 184: [6.5, 31.4], + 185: [6.7, 31.2], + 186: [7, 30.9], + 187: [7.2, 30.7], + 188: [7.4, 30.5], + 189: [7.6, 30.3], + 190: [7.9, 30.1], + 191: [8.1, 29.9], + 192: [8.4, 29.7], + 193: [8.6, 29.6], + 194: [8.9, 29.5], + 195: [9.1, 29.3], + 196: [9.4, 29.2], + 197: [9.6, 29.1], + 198: [9.8, 29], + 199: [10, 28.9], + 200: [10.2, 28.7], + 201: [10.5, 28.7], + 202: [10.7, 28.6], + 203: [11, 28.5], + 204: [11.2, 28.4], + 205: [11.4, 28.3], + 206: [11.6, 28.3], + 207: [11.8, 28.2], + 208: [12.1, 28.1], + 209: [12.3, 28.1], + 210: [12.5, 28], + 211: [12.7, 28], + 212: [12.9, 27.9], + 213: [13.2, 27.8], + 214: [13.4, 27.8], + 215: [13.6, 27.7], + 216: [13.8, 27.7], + 217: [14, 27.7], + 218: [14.3, 27.6], + 219: [14.5, 27.6], + 220: [14.7, 27.5], + 221: [14.9, 27.5], + 222: [15.1, 27.5], + 223: [15.3, 27.4], + 224: [15.5, 27.4], + 225: [15.8, 27.4], + 226: [16, 27.3], + 227: [16.2, 27.3], + 228: [16.4, 27.3], + 229: [16.6, 27.3], + 230: [16.8, 27.2], + 231: [17, 27.2], + 232: [17.2, 27.2], + 233: [17.4, 27.2], + 234: [17.6, 27.2], + 235: [17.8, 27.1], + 236: [18, 27.1], + 237: [18.2, 27.1], + 238: [18.4, 27.1], + 239: [18.7, 27.1], + 240: [18.8, 27], + 241: [19, 27], + 242: [19.2, 27], + 243: [19.4, 27], + 244: [19.6, 27], + 245: [19.8, 27], + 246: [20, 27], + 247: [20.3, 26.9], + 248: [20.5, 26.9], + 249: [20.6, 26.9], + 250: [20.8, 26.9], + 251: [21, 26.9], + 252: [21.3, 26.9], + 253: [21.5, 26.9], + 254: [21.6, 26.9], + 255: [21.8, 26.8], + 256: [22, 26.8], + 257: [22.2, 26.8], + 258: [22.4, 26.8], + 259: [22.6, 26.8], + 260: [22.8, 26.8], + 261: [23, 26.8], + 262: [23.2, 26.8], + 263: [23.4, 26.8], + 264: [23.6, 26.8], + 265: [23.8, 26.8], + 266: [24, 26.8], + 267: [24.1, 26.8], + 268: [24.3, 26.8], + 269: [24.5, 26.8], + 270: [24.7, 26.8], + 271: [24.8, 26.8], + 272: [25.1, 26.7], + 273: [25.3, 26.7], + 274: [25.4, 26.7], + 275: [25.6, 26.7], + 276: [25.8, 26.7], + 277: [26, 26.7], + 278: [26.1, 26.7], + 279: [26.3, 26.7], + 280: [26.5, 26.7], + 281: [26.7, 26.7], + 282: [26.9, 26.7], + 283: [27.1, 26.7], + 284: [27.3, 26.7], + 285: [27.5, 26.7], + 286: [27.7, 26.7], + 287: [27.8, 26.7], + 288: [28, 26.7], + 289: [28.2, 26.7], + 290: [28.4, 26.7], + 291: [28.6, 26.7], + 292: [28.8, 26.7], + 293: [28.9, 26.7], + 294: [29.1, 26.7], + 295: [29.3, 26.7], + 296: [29.5, 26.7], + 297: [29.6, 26.7], + 298: [29.8, 26.7], + 299: [30, 26.7], + 300: [30.2, 26.7], + 301: [30.4, 26.7], + 302: [30.5, 26.7], + 303: [30.7, 26.7], + 304: [30.9, 26.7], + 305: [31.1, 26.7], + 306: [31.2, 26.7], + 307: [31.4, 26.7], + 308: [31.6, 26.7], + 309: [31.8, 26.8], + 310: [31.9, 26.8], + 311: [32.1, 26.8], + 312: [32.3, 26.8], + 313: [32.5, 26.8], + 314: [32.6, 26.8], + 315: [32.8, 26.8], + 316: [33, 26.8], + 317: [33.2, 26.8], + 318: [33.3, 26.8], + 319: [33.5, 26.8], + 320: [33.7, 26.8], + 321: [33.8, 26.8], + 322: [34, 26.8], + 323: [34.2, 26.8], + 324: [34.4, 26.8], + 325: [34.5, 26.8], + 326: [34.7, 26.8], + 327: [34.9, 26.8], + 328: [35.1, 26.8], + 329: [35.2, 26.8], + 330: [35.4, 26.8], + 331: [35.5, 26.8], + 332: [35.7, 26.8], + 333: [35.9, 26.8], + 334: [36.1, 26.8], + 335: [36.3, 26.9], + 336: [36.5, 26.9], + 337: [36.7, 26.9], + 338: [36.9, 26.9], + 339: [37.1, 26.9], + 340: [37.2, 26.9], + 341: [37.4, 26.9], + 342: [37.5, 26.9], + 343: [37.7, 26.9], + 344: [37.9, 26.9], + 345: [38.1, 26.9], + 346: [38.3, 26.9], + 347: [38.5, 26.9], + 348: [38.7, 26.9], + 349: [38.9, 26.9], + 350: [39, 26.9], + 351: [39.2, 26.9], + 352: [39.3, 27], + 353: [39.5, 27], + 354: [39.7, 27], + 355: [39.9, 27], + 356: [40.1, 27], + 357: [40.2, 27], + 358: [40.4, 27], + 359: [40.6, 27], + 360: [40.8, 27], + 361: [40.9, 27], + 362: [41.1, 27], + 363: [41.2, 27], + 364: [41.4, 27], + 365: [41.6, 27], + 366: [41.8, 27], + 367: [42, 27], + 368: [42.1, 27.1], + 369: [42.3, 27.1], + 370: [42.4, 27.1], + 371: [42.6, 27.1], + 372: [42.8, 27.1], + 373: [43, 27.1], + 374: [43.1, 27.1], + 375: [43.2, 27.1], + 376: [43.4, 27.1], + 377: [43.6, 27.1], + 378: [43.8, 27.1], + 379: [43.9, 27.1], + 380: [44.1, 27.1], + 381: [44.3, 27.2], + 382: [44.4, 27.2], + 383: [44.6, 27.2], + 384: [44.7, 27.2], + 385: [44.9, 27.2], + 386: [45.1, 27.2], + 387: [45.3, 27.2], + 388: [45.5, 27.2], + 389: [45.6, 27.2], + 390: [45.8, 27.2], + 391: [46, 27.2], + 392: [46.2, 27.2], + 393: [46.4, 27.3], + 394: [46.5, 27.3], + 395: [46.7, 27.3], + 396: [46.9, 27.3], + 397: [47.1, 27.3], + 398: [47.2, 27.3], + 399: [47.4, 27.3], + 400: [47.6, 27.3], + 401: [47.7, 27.3], + 402: [47.9, 27.3], + 403: [48.1, 27.3], + 404: [48.3, 27.3], + 405: [48.5, 27.4], + 406: [48.7, 27.4], + 407: [48.8, 27.4], + 408: [49, 27.4], + 409: [49.2, 27.4], + 410: [49.4, 27.4], + 411: [49.6, 27.4], + 412: [49.7, 27.4], + 413: [49.9, 27.4], + 414: [50.1, 27.4], + 415: [50.2, 27.4], + 416: [50.4, 27.4], + 417: [50.6, 27.5], + 418: [50.7, 27.5], + 419: [50.9, 27.5], + 420: [51.1, 27.5], + 421: [51.2, 27.5], + 422: [51.4, 27.5], + 423: [51.6, 27.5], + 424: [51.7, 27.5], + 425: [51.9, 27.5], + 426: [52.1, 27.5], + 427: [51.2, 27.6], + 428: [52.4, 27.6], + 429: [52.5, 27.6], + 430: [52.7, 27.6], + 431: [52.9, 27.6], + 432: [53.1, 27.6], + 433: [53.2, 27.6], + 434: [53.4, 27.6], + 435: [53.6, 27.6], + 436: [53.7, 27.6], + 437: [53.9, 27.6], + 438: [54.1, 27.7], + 439: [54.2, 27.7], + 440: [54.3, 27.7], + 441: [54.5, 27.7], + 442: [54.7, 27.7], + 443: [54.8, 27.7], + 444: [55, 27.7], + 445: [55.2, 27.7], + 446: [55.3, 27.7], + 447: [55.5, 27.7], + 448: [55.7, 27.7], + 449: [55.8, 27.8], + 450: [56, 27.8], + 451: [56.2, 27.8], + 452: [56.3, 27.8], + 453: [56.5, 27.8], + 454: [56.7, 27.8], + 455: [56.8, 27.8], + 456: [57, 27.8], + 457: [57.2, 27.8], + 458: [57.3, 27.9], + 459: [57.4, 27.9], + 460: [57.6, 27.9], + 461: [57.8, 27.9], + 462: [57.9, 27.9], + 463: [58.1, 27.9], + 464: [58.3, 27.9], + 465: [58.4, 27.9], + 466: [58.6, 27.9], + 467: [58.8, 27.9], + 468: [59, 28], + 469: [59.1, 28], + 470: [59.2, 28], + 471: [59.4, 28], + 472: [59.6, 28], + 473: [59.7, 28], + 474: [60, 28], + 475: [60.1, 28], + 476: [60.2, 28], + 477: [60.4, 28], + 478: [60.6, 28.1], + 479: [60.7, 28.1], + 480: [60.9, 28.1], + 481: [60.1, 28.1], + 482: [60.3, 28.1], + 483: [61.4, 28.1], + 484: [61.5, 28.1], + 485: [61.7, 28.1], + 486: [61.9, 28.1], + 487: [62, 28.2], + 488: [62.2, 28.2], + 489: [62.3, 28.2], + 490: [62.5, 28.2], + 491: [62.7, 28.2], + 492: [62.8, 28.2], + 493: [63, 28.2], + 494: [63.2, 28.2], + 495: [63.3, 28.2], + 496: [63.4, 28.2], + 497: [63.6, 28.2], + 498: [63.8, 28.3], + 499: [63.9, 28.3], + 500: [64.1, 28.3], + }; + const input = Math.min(Math.max(Math.round(m), 140), 500); + const toReturn = table[input]; + return [Math.round(toReturn[1]), Math.round(toReturn[0])]; +} From 98ab32fa316a19e546641b31c2bbf7280a593746 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 1 May 2024 22:51:52 -0500 Subject: [PATCH 18/73] Update platform.ts --- src/platform.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform.ts b/src/platform.ts index 043dbe78..36fcb0ba 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -635,6 +635,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); break; case 'Hub 2': + case 'remote with screen': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createHub2(device); break; @@ -713,6 +714,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`); break; case 'Remote': + case 'remote with screen+': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId} is Not Supported.`); break; default: From 0adba8a1c3f39dbb5e98319a031c846f08eb4692 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Thu, 2 May 2024 07:36:13 -0500 Subject: [PATCH 19/73] Update platform.ts --- src/platform.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform.ts b/src/platform.ts index 36fcb0ba..67700784 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -635,7 +635,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); break; case 'Hub 2': - case 'remote with screen': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createHub2(device); break; From 774fd07ca77a4de35d90406d1115023501c667d4 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 5 May 2024 22:43:49 -0500 Subject: [PATCH 20/73] Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev) --- CHANGELOG.md | 1 + config.schema.json | 18 ++++ package-lock.json | 175 +++++++++++++++++-------------- package.json | 16 +-- src/device/blindtilt.ts | 33 +++++- src/device/bot.ts | 27 ++++- src/device/ceilinglight.ts | 27 ++++- src/device/colorbulb.ts | 27 ++++- src/device/contact.ts | 28 ++++- src/device/curtain.ts | 27 ++++- src/device/hub.ts | 28 ++++- src/device/humidifier.ts | 27 ++++- src/device/iosensor.ts | 28 ++++- src/device/lightstrip.ts | 27 ++++- src/device/lock.ts | 27 ++++- src/device/meter.ts | 28 ++++- src/device/meterplus.ts | 28 ++++- src/device/motion.ts | 28 ++++- src/device/plug.ts | 27 ++++- src/device/robotvacuumcleaner.ts | 27 ++++- src/device/waterdetector.ts | 28 ++++- src/platform.ts | 28 ++++- src/settings.ts | 2 + 23 files changed, 566 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03751335..827860ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. This projec - Add Support for `Water Detector` - Add BLE support for `Smart Lock` - Add `K10+` deviceType Support +- Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev) - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 diff --git a/config.schema.json b/config.schema.json index 9121ae52..9c7f525d 100644 --- a/config.schema.json +++ b/config.schema.json @@ -858,6 +858,22 @@ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3'));" } }, + "maxRetries": { + "title": "Max Retries for OpenAPI", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Delay Between Retries for OpenAPI (In Milliseconds)", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + } + }, "maxRetry": { "title": "Max Retries for BLE", "type": "number", @@ -1629,6 +1645,8 @@ "options.devices[].hub.hide_lightsensor", "options.devices[].scanDuration", "options.devices[].disableCaching", + "options.devices[].maxRetries", + "options.devices[].delayBetweenRetries", "options.devices[].maxRetry", "options.devices[].bot.mode", "options.devices[].bot.deviceType", diff --git a/package-lock.json b/package-lock.json index 5ef7803f..3f38c6f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,19 +22,19 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.5", + "homebridge-lib": "^7.0.0", "rxjs": "^7.8.1", - "undici": "^6.14.1" + "undici": "^6.15.0" }, "devDependencies": { - "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@types/node": "^20.12.8", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", "eslint": "^8.57.0", - "homebridge": "^1.8.0", - "homebridge-config-ui-x": "4.56.1", + "homebridge": "^1.8.1", + "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", - "npm-check-updates": "^16.14.18", + "npm-check-updates": "^16.14.20", "rimraf": "^5.0.5", "ts-node": "^10.9.2", "typescript": "^5.4.5" @@ -2114,9 +2114,9 @@ } }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2147,16 +2147,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz", - "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", + "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/type-utils": "7.7.1", - "@typescript-eslint/utils": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/type-utils": "7.8.0", + "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.3.1", @@ -2182,15 +2182,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz", - "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", + "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4" }, "engines": { @@ -2210,13 +2210,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", - "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1" + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2227,13 +2227,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz", - "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", + "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/utils": "7.7.1", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/utils": "7.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2254,9 +2254,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", - "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2267,13 +2267,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", - "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2295,17 +2295,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz", - "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.15", "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", "semver": "^7.6.0" }, "engines": { @@ -2320,12 +2320,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", - "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/types": "7.8.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2542,6 +2542,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3068,6 +3069,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3249,6 +3251,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3259,7 +3262,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5106,6 +5110,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5187,12 +5192,12 @@ } }, "node_modules/hb-lib-tools": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", - "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.0.tgz", + "integrity": "sha512-J+ySCF0rUlduEM/5qCZlNhS764JnMEDQ5+qovmKycNZQ2GJjCapMYunGo2DM7B4S+tDudhqIv+SAiO3KNPj1NQ==", "dependencies": { - "bonjour-hap": "^3.7.1", - "chalk": "^4.1.2", + "bonjour-hap": "^3.7.2", + "chalk": "^5.3.0", "semver": "^7.6.0" }, "bin": { @@ -5202,7 +5207,18 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.0||^20||^18" + "node": "20.12.2||^20||^18" + } + }, + "node_modules/hb-lib-tools/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/helmet": { @@ -5275,9 +5291,9 @@ } }, "node_modules/homebridge": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.0.tgz", - "integrity": "sha512-nPb9rPIUIjJ1boxa3h4/ARxmESNyoCEa2gAKP/1BHqSXNMDPXIZNw6nDzCmj15XIda/m1djCE21K+b44BiMJhQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.1.tgz", + "integrity": "sha512-tcyjT79m1SxBHf3bQyI9IEsXN0m1y0GPxPWvwhdJEycdliK0OyuYxzx4w1M5c7PHZknkZbQWCxFyaMTh7DHKUA==", "dev": true, "dependencies": { "chalk": "4.1.2", @@ -5296,9 +5312,9 @@ } }, "node_modules/homebridge-config-ui-x": { - "version": "4.56.1", - "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.56.1.tgz", - "integrity": "sha512-VjOyCZNQG0qLi1GzIh2GSTi2aWtJlx0XZ9yGuupKw0GaY3SvGwLU9900LPqa16OzuPRs6P5BXwS29Gv7r2Ev4w==", + "version": "4.56.2", + "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.56.2.tgz", + "integrity": "sha512-jsWr/EgxUkxznlQ1UxUjZWYc+xl99tWj6n5qqL7Xpe683qjYv5hA71Bj75v6ETwrz2sPttUb2sXMUIZDNvxtDA==", "dev": true, "funding": [ { @@ -5362,12 +5378,12 @@ } }, "node_modules/homebridge-lib": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", - "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.0.tgz", + "integrity": "sha512-vfO2nD6vqus97a/4o0cNsl5v52D8/UO+5odIbCwUalyrxIFjBUey0udJxomK8K19nsdJVfO3gmpGsUPtTMJuHQ==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.1", - "hb-lib-tools": "~1.2.4" + "@homebridge/plugin-ui-utils": "~1.0.3", + "hb-lib-tools": "~2.0.0" }, "bin": { "hap": "cli/hap.js", @@ -5376,8 +5392,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.7.0", - "node": "20.12.0||^20||^18" + "homebridge": "^1.8.1", + "node": "20.12.2||^20||^18" } }, "node_modules/hosted-git-info": { @@ -7234,9 +7250,9 @@ } }, "node_modules/npm-check-updates": { - "version": "16.14.18", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.18.tgz", - "integrity": "sha512-9iaRe9ohx9ykdbLjPRIYcq1A0RkrPYUx9HmQK1JIXhfxtJCNE/+497H9Z4PGH6GWRALbz5KF+1iZoySK2uSEpQ==", + "version": "16.14.20", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.20.tgz", + "integrity": "sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==", "dev": true, "dependencies": { "@types/semver-utils": "^1.1.1", @@ -9694,6 +9710,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10277,9 +10294,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.14.1.tgz", - "integrity": "sha512-mAel3i4BsYhkeVPXeIPXVGPJKeBzqCieZYoFsbWfUzd68JmHByhc1Plit5WlylxXFaGpgkZB8mExlxnt+Q1p7A==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.15.0.tgz", + "integrity": "sha512-VviMt2tlMg1BvQ0FKXxrz1eJuyrcISrL2sPfBf7ZskX/FCEc/7LeThQaoygsMJpNqrATWQIsRVx+1Dpe4jaYuQ==", "engines": { "node": ">=18.17" } diff --git a/package.json b/package.json index 14989cf3..b5ab048c 100644 --- a/package.json +++ b/package.json @@ -80,22 +80,22 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.5", + "homebridge-lib": "^7.0.0", "rxjs": "^7.8.1", - "undici": "^6.14.1" + "undici": "^6.15.0" }, "optionalDependencies": { "node-switchbot": "2.1.0-beta.4" }, "devDependencies": { - "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@types/node": "^20.12.8", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", "eslint": "^8.57.0", - "homebridge": "^1.8.0", - "homebridge-config-ui-x": "4.56.1", + "homebridge": "^1.8.1", + "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", - "npm-check-updates": "^16.14.18", + "npm-check-updates": "^16.14.20", "rimraf": "^5.0.5", "ts-node": "^10.9.2", "typescript": "^5.4.5" diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index e14e9071..0dd6cce8 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -79,6 +79,8 @@ export class BlindTilt { deviceRefreshRate!: number; setCloseMode!: string; setOpenMode!: string; + maxRetries!: number; + delayBetweenRetries!: number; // Updates blindTiltUpdateInProgress!: boolean; @@ -106,6 +108,7 @@ export class BlindTilt { this.scan(device); this.setupMqtt(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); this.mappingMode = (device.blindTilt?.mode as BlindTiltMappingMode) ?? BlindTiltMappingMode.OnlyUp; @@ -594,9 +597,10 @@ export class BlindTilt { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1382,6 +1386,23 @@ export class BlindTilt { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.blindTilt) { @@ -1411,6 +1432,12 @@ export class BlindTilt { if (device.webhook !== undefined) { config['webhook'] = device.webhook; } + if (device.maxRetries !== undefined) { + config['maxRetries'] = device.maxRetries; + } + if (device.delayBetweenRetries !== undefined) { + config['delayBetweenRetries'] = device.delayBetweenRetries; + } if (device.blindTilt?.mode === undefined) { config['mode'] = BlindTiltMappingMode.OnlyUp; } else { diff --git a/src/device/bot.ts b/src/device/bot.ts index 603bcc94..57ab8e00 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -58,6 +58,8 @@ export class Bot { deviceLogging!: string; multiPressCount!: number; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates botUpdateInProgress!: boolean; @@ -88,6 +90,7 @@ export class Bot { this.allowPushChanges(device); this.deviceContext(); this.DoublePress(device); + this.deviceRetry(device); this.deviceConfig(device); this.multiPressCount = 0; @@ -623,9 +626,10 @@ export class Bot { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1483,6 +1487,23 @@ export class Bot { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.bot) { diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index edafaf9a..44c4867b 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -50,6 +50,8 @@ export class CeilingLight { deviceLogging!: string; deviceRefreshRate!: number; adaptiveLightingShift?: number; + maxRetries!: number; + delayBetweenRetries!: number; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; @@ -85,6 +87,7 @@ export class CeilingLight { this.refreshRate(device); this.adaptiveLighting(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -461,9 +464,10 @@ export class CeilingLight { async openAPIRefreshStatus() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1144,6 +1148,23 @@ export class CeilingLight { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.ceilinglight) { diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 5b2b39bc..9880a102 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -58,6 +58,8 @@ export class ColorBulb { deviceLogging!: string; deviceRefreshRate!: number; adaptiveLightingShift?: number; + maxRetries!: number; + delayBetweenRetries!: number; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; @@ -93,6 +95,7 @@ export class ColorBulb { this.refreshRate(device); this.adaptiveLighting(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -527,9 +530,10 @@ export class ColorBulb { async openAPIRefreshStatus() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1353,6 +1357,23 @@ export class ColorBulb { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.colorbulb) { diff --git a/src/device/contact.ts b/src/device/contact.ts index 99e200a8..64f2dba2 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -1,4 +1,3 @@ -import { request } from 'undici'; import { sleep } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; @@ -53,6 +52,8 @@ export class Contact { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates contactUbpdateInProgress!: boolean; @@ -79,6 +80,7 @@ export class Contact { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -437,9 +439,10 @@ export class Contact { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -708,6 +711,23 @@ export class Contact { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.contact) { diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 278f1f07..53fab8cd 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -68,6 +68,8 @@ export class Curtain { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates curtainUpdateInProgress!: boolean; @@ -98,6 +100,7 @@ export class Curtain { this.scan(device); this.setupMqtt(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -634,9 +637,10 @@ export class Curtain { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1310,6 +1314,23 @@ export class Curtain { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.curtain) { diff --git a/src/device/hub.ts b/src/device/hub.ts index c23cfec1..06b16fa0 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -3,7 +3,6 @@ import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, H import { MqttClient } from 'mqtt'; import { hostname } from 'os'; import { interval } from 'rxjs'; -import { request } from 'undici'; import { SwitchBotPlatform } from '../platform.js'; import { Devices, device, deviceStatus, devicesConfig, SwitchBotPlatformConfig } from '../settings.js'; @@ -46,6 +45,8 @@ export class Hub { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Connection private readonly OpenAPI: boolean; @@ -69,6 +70,7 @@ export class Hub { this.deviceContext(); this.setupHistoryService(device); this.setupMqtt(device); + this.deviceRetry(device); this.deviceConfig(device); this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity; @@ -432,9 +434,10 @@ export class Hub { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -742,6 +745,23 @@ export class Hub { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.hub) { diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index ffa9afed..561c98ed 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -50,6 +50,8 @@ export class Humidifier { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates humidifierUpdateInProgress!: boolean; @@ -76,6 +78,7 @@ export class Humidifier { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -429,9 +432,10 @@ export class Humidifier { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1023,6 +1027,23 @@ export class Humidifier { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.humidifier) { diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 786d6ab8..d8b01040 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -1,5 +1,4 @@ import { hostname } from 'os'; -import { request } from 'undici'; import { sleep } from '../utils.js'; import { MqttClient } from 'mqtt'; import asyncmqtt from 'async-mqtt'; @@ -57,6 +56,8 @@ export class IOSensor { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates ioSensorUpdateInProgress!: boolean; @@ -85,6 +86,7 @@ export class IOSensor { this.deviceContext(); this.setupHistoryService(device); this.setupMqtt(device); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -409,9 +411,10 @@ export class IOSensor { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -743,6 +746,23 @@ export class IOSensor { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.meter) { diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index f0416b0e..30dbfb3e 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -48,6 +48,8 @@ export class StripLight { deviceLogging!: string; deviceRefreshRate!: number; adaptiveLightingShift?: number; + maxRetries!: number; + delayBetweenRetries!: number; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; @@ -83,6 +85,7 @@ export class StripLight { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -441,9 +444,10 @@ export class StripLight { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -1172,6 +1176,23 @@ export class StripLight { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.striplight) { diff --git a/src/device/lock.ts b/src/device/lock.ts index 0ee7c2dd..5ad597c0 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -44,6 +44,8 @@ export class Lock { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates lockUpdateInProgress!: boolean; @@ -70,6 +72,7 @@ export class Lock { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -468,9 +471,10 @@ export class Lock { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -847,6 +851,23 @@ export class Lock { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.lock) { diff --git a/src/device/meter.ts b/src/device/meter.ts index 5ff470c6..5b075724 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -3,7 +3,6 @@ import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, H import { MqttClient } from 'mqtt'; import { hostname } from 'os'; import { interval } from 'rxjs'; -import { request } from 'undici'; import { SwitchBotPlatform } from '../platform.js'; import { Devices, device, deviceStatus, devicesConfig, serviceData, temperature, SwitchBotPlatformConfig } from '../settings.js'; import { sleep } from '../utils.js'; @@ -51,6 +50,8 @@ export class Meter { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Connection private readonly OpenAPI: boolean; @@ -75,6 +76,7 @@ export class Meter { this.deviceContext(); this.setupHistoryService(device); this.setupMqtt(device); + this.deviceRetry(device); this.deviceConfig(device); this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity; @@ -398,9 +400,10 @@ export class Meter { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -736,6 +739,23 @@ export class Meter { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.meter) { diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index abd527e0..169de5c3 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -3,7 +3,6 @@ import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, H import { MqttClient } from 'mqtt'; import { hostname } from 'os'; import { interval } from 'rxjs'; -import { request } from 'undici'; import { SwitchBotPlatform } from '../platform.js'; import { Devices, device, deviceStatus, devicesConfig, serviceData, temperature, SwitchBotPlatformConfig } from '../settings.js'; import { sleep } from '../utils.js'; @@ -56,6 +55,8 @@ export class MeterPlus { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Connection private readonly OpenAPI: boolean; @@ -80,6 +81,7 @@ export class MeterPlus { this.deviceContext(); this.setupHistoryService(device); this.setupMqtt(device); + this.deviceRetry(device); this.deviceConfig(device); // Retrieve initial values and updateHomekit @@ -400,9 +402,10 @@ export class MeterPlus { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -751,6 +754,23 @@ export class MeterPlus { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.meter) { diff --git a/src/device/motion.ts b/src/device/motion.ts index 1e12cb29..7d0ae404 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -1,4 +1,3 @@ -import { request } from 'undici'; import { sleep } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; @@ -49,6 +48,8 @@ export class Motion { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates motionUbpdateInProgress!: boolean; @@ -75,6 +76,7 @@ export class Motion { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -392,9 +394,10 @@ export class Motion { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -665,6 +668,23 @@ export class Motion { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.motion) { diff --git a/src/device/plug.ts b/src/device/plug.ts index c9d77c71..73a2d864 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -30,6 +30,8 @@ export class Plug { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates plugUpdateInProgress!: boolean; @@ -56,6 +58,7 @@ export class Plug { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -297,9 +300,10 @@ export class Plug { async openAPIRefreshStatus() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -691,6 +695,23 @@ export class Plug { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.plug) { diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 67523693..7625807c 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -37,6 +37,8 @@ export class RobotVacuumCleaner { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates robotVacuumCleanerUpdateInProgress!: boolean; @@ -63,6 +65,7 @@ export class RobotVacuumCleaner { this.scan(device); this.refreshRate(device); this.deviceContext(); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -344,9 +347,10 @@ export class RobotVacuumCleaner { async openAPIRefreshStatus() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -852,6 +856,23 @@ export class RobotVacuumCleaner { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.plug) { diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 89449a95..9a201248 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -1,5 +1,4 @@ import { hostname } from 'os'; -import { request } from 'undici'; import { sleep } from '../utils.js'; import { MqttClient } from 'mqtt'; import asyncmqtt from 'async-mqtt'; @@ -53,6 +52,8 @@ export class WaterDetector { scanDuration!: number; deviceLogging!: string; deviceRefreshRate!: number; + maxRetries!: number; + delayBetweenRetries!: number; // Updates WaterDetectorUpdateInProgress!: boolean; @@ -81,6 +82,7 @@ export class WaterDetector { this.deviceContext(); this.setupHistoryService(device); this.setupMqtt(device); + this.deviceRetry(device); this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -282,9 +284,10 @@ export class WaterDetector { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { + headers: this.platform.generateHeaders(), + }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -589,6 +592,23 @@ export class WaterDetector { } } + async deviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries === undefined) { + this.maxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); + } else { + this.maxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); + } + if (device.delayBetweenRetries === undefined) { + this.delayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); + } else { + this.delayBetweenRetries = device.delayBetweenRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); + } + } + async deviceConfig(device: device & devicesConfig): Promise { let config = {}; if (device.waterdetector) { diff --git a/src/platform.ts b/src/platform.ts index 67700784..0c6d20f7 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -32,7 +32,7 @@ import { VacuumCleaner } from './irdevice/vacuumcleaner.js'; import { AirConditioner } from './irdevice/airconditioner.js'; import * as http from 'http'; import { Buffer } from 'buffer'; -import { request } from 'undici'; +import { Dispatcher, request } from 'undici'; import { MqttClient } from 'mqtt'; import { queueScheduler } from 'rxjs'; import fakegato from 'fakegato-history'; @@ -40,6 +40,8 @@ import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; import hbLib from 'homebridge-lib'; +import { UrlObject } from 'url'; +import { sleep } from './utils.js'; @@ -2628,6 +2630,30 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } + async retryRequest(deviceMaxRetries: number, deviceDelayBetweenRetries: number, url: string | URL | UrlObject, + options?: { dispatcher?: Dispatcher } & Omit & Partial>): Promise<{ body: any; statusCode: number }> { + let retryCount = 0; + const maxRetries = deviceMaxRetries; + const delayBetweenRetries = deviceDelayBetweenRetries; + while (retryCount < maxRetries) { + try { + const { body, statusCode } = await request(url, options); + if (statusCode === 200 || statusCode === 100) { + return { body, statusCode }; + } else { + this.debugLog(`Received status code: ${statusCode}`); + } + } catch (error: any) { + this.errorLog(`Error making request: ${error.message}`); + } + retryCount++; + this.debugLog(`Retry attempt ${retryCount} of ${maxRetries}`); + await sleep(delayBetweenRetries); + } + return { body: null, statusCode: -1 }; + } + // BLE Connection async connectBLE() { let switchbot: any; diff --git a/src/settings.ts b/src/settings.ts index cb66398b..1fd6ef42 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -59,6 +59,8 @@ export interface devicesConfig extends device { hide_device?: boolean; offline?: boolean; maxRetry?: number; + maxRetries?: number; + delayBetweenRetries?: number; disableCaching?: boolean; mqttURL?: string; mqttOptions?: IClientOptions; From bab3e38d2ed84904f9e0c4a0a557242b9844cacb Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 5 May 2024 23:01:53 -0500 Subject: [PATCH 21/73] fix dependency --- package-lock.json | 47 +++++++++++++++-------------------------------- package.json | 2 +- 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f38c6f4..482c1754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^7.0.0", + "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", "undici": "^6.15.0" }, @@ -2542,7 +2542,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3069,7 +3068,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3251,7 +3249,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3262,8 +3259,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-support": { "version": "1.1.3", @@ -5110,7 +5106,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5192,12 +5187,12 @@ } }, "node_modules/hb-lib-tools": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.0.tgz", - "integrity": "sha512-J+ySCF0rUlduEM/5qCZlNhS764JnMEDQ5+qovmKycNZQ2GJjCapMYunGo2DM7B4S+tDudhqIv+SAiO3KNPj1NQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", + "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", "dependencies": { - "bonjour-hap": "^3.7.2", - "chalk": "^5.3.0", + "bonjour-hap": "^3.7.1", + "chalk": "^4.1.2", "semver": "^7.6.0" }, "bin": { @@ -5207,18 +5202,7 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.2||^20||^18" - } - }, - "node_modules/hb-lib-tools/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "20.12.0||^20||^18" } }, "node_modules/helmet": { @@ -5378,12 +5362,12 @@ } }, "node_modules/homebridge-lib": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.0.tgz", - "integrity": "sha512-vfO2nD6vqus97a/4o0cNsl5v52D8/UO+5odIbCwUalyrxIFjBUey0udJxomK8K19nsdJVfO3gmpGsUPtTMJuHQ==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", + "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.3", - "hb-lib-tools": "~2.0.0" + "@homebridge/plugin-ui-utils": "~1.0.1", + "hb-lib-tools": "~1.2.4" }, "bin": { "hap": "cli/hap.js", @@ -5392,8 +5376,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.8.1", - "node": "20.12.2||^20||^18" + "homebridge": "^1.7.0", + "node": "20.12.0||^20||^18" } }, "node_modules/hosted-git-info": { @@ -9710,7 +9694,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index b5ab048c..9f8c9fe4 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^7.0.0", + "homebridge-lib": "^6.7.5", "rxjs": "^7.8.1", "undici": "^6.15.0" }, From cd0bb883ffca9e6c30bbd5147f5a9114bba2d5e5 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 5 May 2024 23:02:12 -0500 Subject: [PATCH 22/73] add retry to platform --- src/platform.ts | 194 +++++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 91 deletions(-) diff --git a/src/platform.ts b/src/platform.ts index 0c6d20f7..1e2cb293 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -494,44 +494,95 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { */ async discoverDevices() { if (this.config.credentials?.token) { - try { - const { body, statusCode } = await request(Devices, { - headers: this.generateHeaders(), - }); - this.debugWarnLog(`statusCode: ${statusCode}`); - const devicesAPI: any = await body.json(); - this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`); - this.debugWarnLog(`devicesAPI Body: ${JSON.stringify(devicesAPI.body)}`); - this.debugWarnLog(`devicesAPI StatusCode: ${devicesAPI.statusCode}`); - if ((statusCode === 200 || statusCode === 100) && (devicesAPI.statusCode === 200 || devicesAPI.statusCode === 100)) { - this.debugErrorLog(`statusCode: ${statusCode} & devicesAPI StatusCode: ${devicesAPI.statusCode}`); - // SwitchBot Devices - const deviceLists = devicesAPI.body.deviceList; - this.debugWarnLog(`DeviceLists: ${JSON.stringify(deviceLists)}`); - this.debugWarnLog(`DeviceLists Length: ${deviceLists.length}`); - if (!this.config.options?.devices) { - this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`); - if (deviceLists.length === 0) { - this.debugLog(`SwitchBot API Currently Doesn't Have Any Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`); - } else { - const devices = deviceLists.map((v: any) => v); - for (const device of devices) { - if (device.deviceType) { - if (device.configDeviceName) { - device.deviceName = device.configDeviceName; + let retryCount = 0; + const maxRetries = 5; // Maximum number of retries + const delayBetweenRetries = 5000; // Delay between retries in milliseconds + while (retryCount < maxRetries) { + try { + const { body, statusCode } = await request(Devices, { + headers: this.generateHeaders(), + }); + this.debugWarnLog(`statusCode: ${statusCode}`); + const devicesAPI: any = await body.json(); + this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`); + this.debugWarnLog(`devicesAPI Body: ${JSON.stringify(devicesAPI.body)}`); + this.debugWarnLog(`devicesAPI StatusCode: ${devicesAPI.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (devicesAPI.statusCode === 200 || devicesAPI.statusCode === 100)) { + this.debugErrorLog(`statusCode: ${statusCode} & devicesAPI StatusCode: ${devicesAPI.statusCode}`); + // SwitchBot Devices + const deviceLists = devicesAPI.body.deviceList; + this.debugWarnLog(`DeviceLists: ${JSON.stringify(deviceLists)}`); + this.debugWarnLog(`DeviceLists Length: ${deviceLists.length}`); + if (!this.config.options?.devices) { + this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`); + if (deviceLists.length === 0) { + this.debugLog(`SwitchBot API Currently Doesn't Have Any Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`); + } else { + const devices = deviceLists.map((v: any) => v); + for (const device of devices) { + if (device.deviceType) { + if (device.configDeviceName) { + device.deviceName = device.configDeviceName; + } + this.createDevice(device); + } + } + } + } else if (this.config.credentials?.token && this.config.options.devices) { + this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`); + if (deviceLists.length === 0) { + this.debugLog(`SwitchBot API Currently Doesn't Have Any Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`); + } else { + const deviceConfigs = this.config.options?.devices; + + 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.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item, + ), + ...itm, + })); + + const devices = mergeBydeviceId(deviceLists, deviceConfigs); + this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`); + for (const device of devices) { + if (!device.deviceType) { + device.deviceType = device.configDeviceType; + this.errorLog(`API has displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`); + } + if (device.deviceType) { + if (device.configDeviceName) { + device.deviceName = device.configDeviceName; + } + this.createDevice(device); } - this.createDevice(device); } } + } else { + this.errorLog('SwitchBot Token Supplied, Issue with Auth.'); + } + if (devicesAPI.body.deviceList.length !== 0) { + this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`); + } else { + this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`); } - } else if (this.config.credentials?.token && this.config.options.devices) { - this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`); - if (deviceLists.length === 0) { - this.debugLog(`SwitchBot API Currently Doesn't Have Any Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`); + + // IR Devices + const irDeviceLists = devicesAPI.body.infraredRemoteList; + if (!this.config.options?.irdevices) { + this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`); + const devices = irDeviceLists.map((v: any) => v); + for (const device of devices) { + if (device.remoteType) { + this.createIRDevice(device); + } + } } else { - const deviceConfigs = this.config.options?.devices; + this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`); + const irDeviceConfig = this.config.options?.irdevices; - const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) => + const mergeIRBydeviceId = (a1: { deviceId: string }[], a2: any[]) => a1.map((itm: { deviceId: string }) => ({ ...a2.find( (item: { deviceId: string }) => @@ -540,73 +591,34 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ...itm, })); - const devices = mergeBydeviceId(deviceLists, deviceConfigs); - this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`); + const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig); + this.debugLog(`IR Devices: ${JSON.stringify(devices)}`); for (const device of devices) { - if (!device.deviceType) { - device.deviceType = device.configDeviceType; - this.errorLog(`API has displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`); - } - if (device.deviceType) { - if (device.configDeviceName) { - device.deviceName = device.configDeviceName; - } - this.createDevice(device); - } - } - } - } else { - this.errorLog('SwitchBot Token Supplied, Issue with Auth.'); - } - if (devicesAPI.body.deviceList.length !== 0) { - this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`); - } else { - this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`); - } - - // IR Devices - const irDeviceLists = devicesAPI.body.infraredRemoteList; - if (!this.config.options?.irdevices) { - this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`); - const devices = irDeviceLists.map((v: any) => v); - for (const device of devices) { - if (device.remoteType) { this.createIRDevice(device); } } - } else { - this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`); - const irDeviceConfig = this.config.options?.irdevices; - - const mergeIRBydeviceId = (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.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item, - ), - ...itm, - })); - - const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig); - this.debugLog(`IR Devices: ${JSON.stringify(devices)}`); - for (const device of devices) { - this.createIRDevice(device); + if (devicesAPI.body.infraredRemoteList.length !== 0) { + this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`); + } else { + this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`); } - } - if (devicesAPI.body.infraredRemoteList.length !== 0) { - this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`); + break; } else { - this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`); + this.statusCode(statusCode); + this.statusCode(devicesAPI.statusCode); + if (statusCode === 500) { + retryCount++; + this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount} of ${maxRetries}`); + await sleep(delayBetweenRetries); + } } - } else { - this.statusCode(statusCode); - this.statusCode(devicesAPI.statusCode); + } catch (e: any) { + retryCount++; + this.debugErrorLog( + `Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug', + ); + this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`); } - } catch (e: any) { - this.debugErrorLog( - `Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug', - ); - this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`); } } else if (!this.config.credentials?.token && this.config.options?.devices) { this.debugLog(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`); From fa6fcbcb1687aa4f7a5c840357d68960a7a8c8a1 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 5 May 2024 23:26:32 -0500 Subject: [PATCH 23/73] a few more changes --- config.schema.json | 20 +++++++++++--- package-lock.json | 47 ++++++++++++++++++++++---------- package.json | 2 +- src/device/blindtilt.ts | 6 ++-- src/device/bot.ts | 6 ++-- src/device/ceilinglight.ts | 6 ++-- src/device/colorbulb.ts | 6 ++-- src/device/contact.ts | 6 ++-- src/device/curtain.ts | 6 ++-- src/device/hub.ts | 6 ++-- src/device/humidifier.ts | 6 ++-- src/device/iosensor.ts | 6 ++-- src/device/lightstrip.ts | 6 ++-- src/device/lock.ts | 6 ++-- src/device/meter.ts | 6 ++-- src/device/meterplus.ts | 6 ++-- src/device/motion.ts | 6 ++-- src/device/plug.ts | 6 ++-- src/device/robotvacuumcleaner.ts | 6 ++-- src/device/waterdetector.ts | 6 ++-- src/platform.ts | 24 +++++++++++++--- src/settings.ts | 2 ++ 22 files changed, 122 insertions(+), 75 deletions(-) diff --git a/config.schema.json b/config.schema.json index 9c7f525d..7982d83c 100644 --- a/config.schema.json +++ b/config.schema.json @@ -859,17 +859,17 @@ } }, "maxRetries": { - "title": "Max Retries for OpenAPI", + "title": "Device Max Retries for OpenAPI", "type": "number", - "placeholder": "5", + "placeholder": 5, "condition": { "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" } }, "delayBetweenRetries": { - "title": "Delay Between Retries for OpenAPI (In Milliseconds)", + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", "type": "number", - "placeholder": "5", + "placeholder": 3, "condition": { "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" } @@ -1555,6 +1555,16 @@ "type": "string", "placeholder": "http://${FQDN}:${PORT}/${PATH}" }, + "maxRetries": { + "title": "Max Retries for OpenAPI", + "type": "number", + "placeholder": 5 + }, + "delayBetweenRetries": { + "title": "Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3 + }, "refreshRate": { "title": "Refresh Rate", "type": "number", @@ -1756,6 +1766,8 @@ "expanded": false, "items": [ "options.webhookURL", + "options.maxRetries", + "options.delayBetweenRetries", { "type": "help", "helpvalue": "
Refresh Rate
Refresh Rate indicates the number of seconds between polls of SwitchBot API." diff --git a/package-lock.json b/package-lock.json index 482c1754..3f38c6f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.5", + "homebridge-lib": "^7.0.0", "rxjs": "^7.8.1", "undici": "^6.15.0" }, @@ -2542,6 +2542,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3068,6 +3069,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3249,6 +3251,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3259,7 +3262,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5106,6 +5110,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5187,12 +5192,12 @@ } }, "node_modules/hb-lib-tools": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", - "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.0.tgz", + "integrity": "sha512-J+ySCF0rUlduEM/5qCZlNhS764JnMEDQ5+qovmKycNZQ2GJjCapMYunGo2DM7B4S+tDudhqIv+SAiO3KNPj1NQ==", "dependencies": { - "bonjour-hap": "^3.7.1", - "chalk": "^4.1.2", + "bonjour-hap": "^3.7.2", + "chalk": "^5.3.0", "semver": "^7.6.0" }, "bin": { @@ -5202,7 +5207,18 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.0||^20||^18" + "node": "20.12.2||^20||^18" + } + }, + "node_modules/hb-lib-tools/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/helmet": { @@ -5362,12 +5378,12 @@ } }, "node_modules/homebridge-lib": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", - "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.0.tgz", + "integrity": "sha512-vfO2nD6vqus97a/4o0cNsl5v52D8/UO+5odIbCwUalyrxIFjBUey0udJxomK8K19nsdJVfO3gmpGsUPtTMJuHQ==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.1", - "hb-lib-tools": "~1.2.4" + "@homebridge/plugin-ui-utils": "~1.0.3", + "hb-lib-tools": "~2.0.0" }, "bin": { "hap": "cli/hap.js", @@ -5376,8 +5392,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.7.0", - "node": "20.12.0||^20||^18" + "homebridge": "^1.8.1", + "node": "20.12.2||^20||^18" } }, "node_modules/hosted-git-info": { @@ -9694,6 +9710,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index 9f8c9fe4..b5ab048c 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^6.7.5", + "homebridge-lib": "^7.0.0", "rxjs": "^7.8.1", "undici": "^6.15.0" }, diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 0dd6cce8..3cf2a608 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -1387,18 +1387,18 @@ export class BlindTilt { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/bot.ts b/src/device/bot.ts index 57ab8e00..661f437e 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -1488,18 +1488,18 @@ export class Bot { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 44c4867b..15f5afb8 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -1149,18 +1149,18 @@ export class CeilingLight { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 9880a102..d1b83bde 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -1358,18 +1358,18 @@ export class ColorBulb { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/contact.ts b/src/device/contact.ts index 64f2dba2..bf90d719 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -712,18 +712,18 @@ export class Contact { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 53fab8cd..60983603 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -1315,18 +1315,18 @@ export class Curtain { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/hub.ts b/src/device/hub.ts index 06b16fa0..c9f2815d 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -746,18 +746,18 @@ export class Hub { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 561c98ed..8cc55062 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -1028,18 +1028,18 @@ export class Humidifier { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index d8b01040..cfa8ae54 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -747,18 +747,18 @@ export class IOSensor { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 30dbfb3e..9faad360 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -1177,18 +1177,18 @@ export class StripLight { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/lock.ts b/src/device/lock.ts index 5ad597c0..02dd84ce 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -852,18 +852,18 @@ export class Lock { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 5b075724..0c5d4b9a 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -740,18 +740,18 @@ export class Meter { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 169de5c3..3db09af5 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -755,18 +755,18 @@ export class MeterPlus { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/motion.ts b/src/device/motion.ts index 7d0ae404..06e89bc7 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -669,18 +669,18 @@ export class Motion { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/plug.ts b/src/device/plug.ts index 73a2d864..1424a549 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -696,18 +696,18 @@ export class Plug { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 7625807c..fa1c4794 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -857,18 +857,18 @@ export class RobotVacuumCleaner { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 9a201248..3970215a 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -593,18 +593,18 @@ export class WaterDetector { } async deviceRetry(device: device & devicesConfig): Promise { - if (device.maxRetries === undefined) { + if (!device.maxRetries) { this.maxRetries = 5; // Maximum number of retries this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); } else { this.maxRetries = device.maxRetries; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); } - if (device.delayBetweenRetries === undefined) { + if (!device.delayBetweenRetries) { this.delayBetweenRetries = 3000; // Delay between retries in milliseconds this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); } else { - this.delayBetweenRetries = device.delayBetweenRetries; + this.delayBetweenRetries = device.delayBetweenRetries * 1000; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); } } diff --git a/src/platform.ts b/src/platform.ts index 1e2cb293..fe8ddb51 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -39,7 +39,7 @@ import fakegato from 'fakegato-history'; import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; -import hbLib from 'homebridge-lib'; +import { EveHomeKitTypes } from 'homebridge-lib'; import { UrlObject } from 'url'; import { sleep } from './utils.js'; @@ -58,6 +58,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { version!: string; Logging?: string; debugMode!: boolean; + maxRetries!: number; + delayBetweenRetries!: number; platformConfig!: SwitchBotPlatformConfig['options']; platformLogging!: SwitchBotPlatformConfig['logging']; config!: SwitchBotPlatformConfig; @@ -106,7 +108,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } // import fakegato-history module and EVE characteristics - const { EveHomeKitTypes } = hbLib; this.fakegatoAPI = fakegato(api); this.eve = new EveHomeKitTypes(api); @@ -385,6 +386,21 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugWarnLog('Using Default Push Rate.'); } + if (!this.config.options.maxRetries) { + this.config.options!.maxRetries! = 5; + this.debugWarnLog('Using Default Max Retries.'); + } else { + this.maxRetries = this.config.options.maxRetries; + } + + if (!this.config.options.delayBetweenRetries) { + // default 3 seconds + this.config.options!.delayBetweenRetries! = 3000; + this.debugWarnLog('Using Default Delay Between Retries.'); + } else { + this.delayBetweenRetries = this.config.options.delayBetweenRetries * 1000; + } + if (!this.config.credentials && !this.config.options) { this.debugWarnLog('Missing Credentials'); } else if (this.config.credentials && !this.config.credentials.notice) { @@ -495,8 +511,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { async discoverDevices() { if (this.config.credentials?.token) { let retryCount = 0; - const maxRetries = 5; // Maximum number of retries - const delayBetweenRetries = 5000; // Delay between retries in milliseconds + const maxRetries = this.maxRetries; // Maximum number of retries + const delayBetweenRetries = this.delayBetweenRetries; // Delay between retries in milliseconds while (retryCount < maxRetries) { try { const { body, statusCode } = await request(Devices, { diff --git a/src/settings.ts b/src/settings.ts index 1fd6ef42..83fc29a3 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -36,6 +36,8 @@ export type credentials = { export type options = { refreshRate?: number; pushRate?: number; + maxRetries?: number; + delayBetweenRetries?: number; logging?: string; devices?: Array; irdevices?: Array; From 79afebb7b60de1ec84acdaecf37c98b1825a90e1 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 5 May 2024 23:32:34 -0500 Subject: [PATCH 24/73] fix lib again --- package-lock.json | 47 +++++++++++++++-------------------------------- package.json | 2 +- src/platform.ts | 3 ++- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f38c6f4..175d7b1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^7.0.0", + "homebridge-lib": "6.7.5", "rxjs": "^7.8.1", "undici": "^6.15.0" }, @@ -2542,7 +2542,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3069,7 +3068,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3251,7 +3249,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3262,8 +3259,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-support": { "version": "1.1.3", @@ -5110,7 +5106,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5192,12 +5187,12 @@ } }, "node_modules/hb-lib-tools": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.0.tgz", - "integrity": "sha512-J+ySCF0rUlduEM/5qCZlNhS764JnMEDQ5+qovmKycNZQ2GJjCapMYunGo2DM7B4S+tDudhqIv+SAiO3KNPj1NQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", + "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", "dependencies": { - "bonjour-hap": "^3.7.2", - "chalk": "^5.3.0", + "bonjour-hap": "^3.7.1", + "chalk": "^4.1.2", "semver": "^7.6.0" }, "bin": { @@ -5207,18 +5202,7 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.2||^20||^18" - } - }, - "node_modules/hb-lib-tools/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "20.12.0||^20||^18" } }, "node_modules/helmet": { @@ -5378,12 +5362,12 @@ } }, "node_modules/homebridge-lib": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.0.tgz", - "integrity": "sha512-vfO2nD6vqus97a/4o0cNsl5v52D8/UO+5odIbCwUalyrxIFjBUey0udJxomK8K19nsdJVfO3gmpGsUPtTMJuHQ==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", + "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.3", - "hb-lib-tools": "~2.0.0" + "@homebridge/plugin-ui-utils": "~1.0.1", + "hb-lib-tools": "~1.2.4" }, "bin": { "hap": "cli/hap.js", @@ -5392,8 +5376,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.8.1", - "node": "20.12.2||^20||^18" + "homebridge": "^1.7.0", + "node": "20.12.0||^20||^18" } }, "node_modules/hosted-git-info": { @@ -9710,7 +9694,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index b5ab048c..387e718f 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "^7.0.0", + "homebridge-lib": "6.7.5", "rxjs": "^7.8.1", "undici": "^6.15.0" }, diff --git a/src/platform.ts b/src/platform.ts index fe8ddb51..22cd4fba 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -39,7 +39,7 @@ import fakegato from 'fakegato-history'; import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; -import { EveHomeKitTypes } from 'homebridge-lib'; +import hbLib from 'homebridge-lib'; import { UrlObject } from 'url'; import { sleep } from './utils.js'; @@ -108,6 +108,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } // import fakegato-history module and EVE characteristics + const { EveHomeKitTypes } = hbLib; this.fakegatoAPI = fakegato(api); this.eve = new EveHomeKitTypes(api); From a2a80ceb55985b349eee840b33fb9992beb9bbf0 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 6 May 2024 17:34:01 -0500 Subject: [PATCH 25/73] update node-switchbot beta --- package-lock.json | 62 +++++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 175d7b1d..5a12c585 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "node": "^18 || ^20" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.4" + "node-switchbot": "2.1.0-beta.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -82,9 +82,9 @@ } }, "node_modules/@abandonware/noble": { - "version": "1.9.2-24", - "resolved": "https://registry.npmjs.org/@abandonware/noble/-/noble-1.9.2-24.tgz", - "integrity": "sha512-9kP3DWP1IxuG5PYjz4rqGXl3u6JIu654iibR6Teasgk5UsdH9nkTi2teYuUl54yoOz6vjgXNjofwXp3naJvhfQ==", + "version": "1.9.2-25", + "resolved": "https://registry.npmjs.org/@abandonware/noble/-/noble-1.9.2-25.tgz", + "integrity": "sha512-FBgwjWkXF3VLb0V0hdOeeVhuupdfuagkBz9E0Xjfmr8MYuR5JjnXHty0/6Q38d4v//EhX7oZaZXBiZWrhWKKbg==", "hasInstallScript": true, "optional": true, "os": [ @@ -998,9 +998,9 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "optional": true, "engines": { "node": "14 || >=16.14" @@ -2943,9 +2943,9 @@ } }, "node_modules/cacache": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", - "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==", + "version": "18.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", + "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", "optional": true, "dependencies": { "@npmcli/fs": "^3.1.0", @@ -2966,9 +2966,9 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "optional": true, "engines": { "node": "14 || >=16.14" @@ -6503,9 +6503,9 @@ "dev": true }, "node_modules/make-fetch-happen": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz", - "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", "optional": true, "dependencies": { "@npmcli/agent": "^2.0.0", @@ -6517,6 +6517,7 @@ "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", "ssri": "^10.0.0" }, @@ -6524,6 +6525,15 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -6984,9 +6994,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -7013,9 +7023,9 @@ } }, "node_modules/node-gyp/node_modules/nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "optional": true, "dependencies": { "abbrev": "^2.0.0" @@ -7067,12 +7077,12 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.0-beta.4", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.4.tgz", - "integrity": "sha512-nzqiHeNB9EyJ+2aDsfkjuiU8/OgHO6NgNU9g8qgP/qF0k3TYXb+qK6EKyd9lyWZlEy6oG80xdaU9PH0I095KpQ==", + "version": "2.1.0-beta.6", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.6.tgz", + "integrity": "sha512-BkwjTR8lRAMfXmRoDNkVtmJmT0p80s3BNiMfBFs1O0iJGM1wiTxljIs6Q43GLIvfUKbx21L0/JGXAS//FVrSCA==", "optional": true, "dependencies": { - "@abandonware/noble": "^1.9.2-24" + "@abandonware/noble": "^1.9.2-25" }, "optionalDependencies": { "@abandonware/bluetooth-hci-socket": "^0.5.3-12" diff --git a/package.json b/package.json index 387e718f..68f9ca38 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "undici": "^6.15.0" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.4" + "node-switchbot": "2.1.0-beta.6" }, "devDependencies": { "@types/node": "^20.12.8", From 858db627fae70dfa9f82ba4eef9742345d163bda Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 14 May 2024 15:35:53 -0500 Subject: [PATCH 26/73] refactor deviceBases --- config.schema.json | 25 +- package-lock.json | 200 ++-- package.json | 14 +- src/device/blindtilt.ts | 1165 ++++++++---------------- src/device/bot.ts | 1466 ++++++++++++------------------ src/device/ceilinglight.ts | 810 ++++------------- src/device/colorbulb.ts | 918 +++++-------------- src/device/contact.ts | 795 +++++----------- src/device/curtain.ts | 1180 +++++++----------------- src/device/device.ts | 655 +++++++++++++ src/device/hub.ts | 882 +++++------------- src/device/humidifier.ts | 869 +++++------------- src/device/iosensor.ts | 806 ++++------------ src/device/lightstrip.ts | 852 +++++------------ src/device/lock.ts | 843 +++++------------ src/device/meter.ts | 793 ++++------------ src/device/meterplus.ts | 799 ++++------------ src/device/motion.ts | 710 +++------------ src/device/plug.ts | 586 ++---------- src/device/robotvacuumcleaner.ts | 633 +++---------- src/device/waterdetector.ts | 632 +++---------- src/irdevice/airconditioner.ts | 581 +++++------- src/irdevice/airpurifier.ts | 305 +------ src/irdevice/camera.ts | 295 +----- src/irdevice/fan.ts | 293 +----- src/irdevice/irdevice.ts | 518 +++++++++++ src/irdevice/light.ts | 301 +----- src/irdevice/other.ts | 1015 ++++++++------------- src/irdevice/tv.ts | 441 ++------- src/irdevice/vacuumcleaner.ts | 356 +------- src/irdevice/waterheater.ts | 362 +------- src/settings.ts | 23 +- src/utils.ts | 71 ++ 33 files changed, 6189 insertions(+), 14005 deletions(-) create mode 100644 src/device/device.ts create mode 100644 src/irdevice/irdevice.ts diff --git a/config.schema.json b/config.schema.json index 7982d83c..9b1a9240 100644 --- a/config.schema.json +++ b/config.schema.json @@ -478,14 +478,33 @@ "title": "Hide Meter's Temperature Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor') && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" } }, "hide_humidity": { "title": "Hide Meter's Humidity Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor') && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" + } + } + } + }, + "iosensor": { + "type": "object", + "properties": { + "hide_temperature": { + "title": "Hide Indoor/Outdoor's Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId);" + } + }, + "hide_humidity": { + "title": "Hide Indoor/Outdoor's Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId);" } } } @@ -1665,6 +1684,8 @@ "options.devices[].bot.pushRatePress", "options.devices[].meter.hide_temperature", "options.devices[].meter.hide_humidity", + "options.devices[].iosensor.hide_temperature", + "options.devices[].iosensor.hide_humidity", "options.devices[].waterdetector.hide_leak", "options.devices[].humidifier.set_minStep", "options.devices[].humidifier.hide_temperature", diff --git a/package-lock.json b/package-lock.json index 5a12c585..fcb34257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,20 +22,20 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "6.7.5", + "homebridge-lib": "7.0.1", "rxjs": "^7.8.1", - "undici": "^6.15.0" + "undici": "^6.16.1" }, "devDependencies": { - "@types/node": "^20.12.8", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", + "@types/node": "^20.12.12", + "@typescript-eslint/eslint-plugin": "^7.9.0", + "@typescript-eslint/parser": "^7.9.0", "eslint": "^8.57.0", "homebridge": "^1.8.1", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", - "rimraf": "^5.0.5", + "rimraf": "^5.0.7", "ts-node": "^10.9.2", "typescript": "^5.4.5" }, @@ -44,7 +44,7 @@ "node": "^18 || ^20" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.6" + "node-switchbot": "2.1.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2098,12 +2098,6 @@ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, "node_modules/@types/jsonwebtoken": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", @@ -2114,20 +2108,14 @@ } }, "node_modules/@types/node": { - "version": "20.12.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", - "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, "node_modules/@types/semver-utils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", @@ -2147,21 +2135,19 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", - "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", + "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/type-utils": "7.8.0", - "@typescript-eslint/utils": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/type-utils": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -2182,15 +2168,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4" }, "engines": { @@ -2210,13 +2196,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", - "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0" + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2227,13 +2213,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", - "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", + "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2254,9 +2240,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", - "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2267,13 +2253,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", - "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2295,18 +2281,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", - "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2320,12 +2303,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", - "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/types": "7.9.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2542,6 +2525,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3068,6 +3052,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3249,6 +3234,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3259,7 +3245,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5106,6 +5093,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5187,13 +5175,13 @@ } }, "node_modules/hb-lib-tools": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", - "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.1.tgz", + "integrity": "sha512-okOzIvlR4QgfyCh7qIOczRxJDuv3TdzvWTMFlYK8c4UTSXHnIb/dWBvkud7tQyXa9n8FA2DBTJdAUSWJLM63sA==", "dependencies": { - "bonjour-hap": "^3.7.1", - "chalk": "^4.1.2", - "semver": "^7.6.0" + "bonjour-hap": "^3.7.2", + "chalk": "^5.3.0", + "semver": "^7.6.2" }, "bin": { "hap": "cli/hap.js", @@ -5202,7 +5190,29 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.0||^20||^18" + "node": "20.13.1||^20||^18" + } + }, + "node_modules/hb-lib-tools/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/hb-lib-tools/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/helmet": { @@ -5362,12 +5372,12 @@ } }, "node_modules/homebridge-lib": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", - "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.1.tgz", + "integrity": "sha512-OMwQEx1Uhb67VsFoY+s9KXUjc6g7XQP86GrNEvPD7ldnaA/D0HN3KHfsaJ0JyZaNCvUOEqG8A4youCMj73RSSw==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.1", - "hb-lib-tools": "~1.2.4" + "@homebridge/plugin-ui-utils": "~1.0.3", + "hb-lib-tools": "~2.0.1" }, "bin": { "hap": "cli/hap.js", @@ -5376,8 +5386,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.7.0", - "node": "20.12.0||^20||^18" + "homebridge": "^1.8.1", + "node": "20.13.1||^20||^18" } }, "node_modules/hosted-git-info": { @@ -7077,9 +7087,9 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.0-beta.6", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0-beta.6.tgz", - "integrity": "sha512-BkwjTR8lRAMfXmRoDNkVtmJmT0p80s3BNiMfBFs1O0iJGM1wiTxljIs6Q43GLIvfUKbx21L0/JGXAS//FVrSCA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0.tgz", + "integrity": "sha512-KHjZJTLQrgqaUqjqOf/1C4docddRslBLSFkqvgHzzzvsyJX0NDu+y2J9/Bs2miZvUwGjGGJNRx//5ertHstjEA==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-25" @@ -8853,9 +8863,9 @@ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" }, "node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", "dev": true, "dependencies": { "glob": "^10.3.7" @@ -8864,7 +8874,7 @@ "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8960,6 +8970,7 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9704,6 +9715,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10287,9 +10299,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.15.0.tgz", - "integrity": "sha512-VviMt2tlMg1BvQ0FKXxrz1eJuyrcISrL2sPfBf7ZskX/FCEc/7LeThQaoygsMJpNqrATWQIsRVx+1Dpe4jaYuQ==", + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.16.1.tgz", + "integrity": "sha512-NeNiTT7ixpeiL1qOIU/xTVpHpVP0svmI6PwoCKaMGaI5AsHOaRdwqU/f7Fi9eyU4u03nd5U/BC8wmRMnS9nqoA==", "engines": { "node": ">=18.17" } diff --git a/package.json b/package.json index 68f9ca38..7530f5b3 100644 --- a/package.json +++ b/package.json @@ -80,23 +80,23 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "6.7.5", + "homebridge-lib": "7.0.1", "rxjs": "^7.8.1", - "undici": "^6.15.0" + "undici": "^6.16.1" }, "optionalDependencies": { - "node-switchbot": "2.1.0-beta.6" + "node-switchbot": "2.1.0" }, "devDependencies": { - "@types/node": "^20.12.8", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", + "@types/node": "^20.12.12", + "@typescript-eslint/eslint-plugin": "^7.9.0", + "@typescript-eslint/parser": "^7.9.0", "eslint": "^8.57.0", "homebridge": "^1.8.1", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", - "rimraf": "^5.0.5", + "rimraf": "^5.0.7", "ts-node": "^10.9.2", "typescript": "^5.4.5" } diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 3cf2a608..f4f99cc3 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -1,116 +1,57 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * blindtilt.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { sleep } from '../utils.js'; -import { MqttClient } from 'mqtt'; +import { BlindTiltMappingMode } from '../utils.js'; import { interval, Subject } from 'rxjs'; -import asyncmqtt from 'async-mqtt'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, Logging, API, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; - -enum BlindTiltMappingMode { - OnlyUp = 'only_up', - OnlyDown = 'only_down', - DownAndUp = 'down_and_up', - UpAndDown = 'up_and_down', - UseTiltForDirection = 'use_tilt_for_direction', -} +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + -export class BlindTilt { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class BlindTilt extends deviceBase { // Services - batteryService!: Service; - lightSensorService?: Service; - windowCoveringService!: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - PositionState!: CharacteristicValue; - TargetPosition!: CharacteristicValue; - CurrentPosition!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - CurrentAmbientLightLevel?: CharacteristicValue; - TargetHorizontalTiltAngle!: CharacteristicValue; - CurrentHorizontalTiltAngle!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_InMotion: deviceStatus['moving']; - OpenAPI_BatteryLevel: serviceData['battery']; - OpenAPI_Direction: deviceStatus['direction']; - OpenAPI_Calibration: serviceData['calibration']; - OpenAPI_CurrentPosition: serviceData['position']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness']; + private WindowCovering: { + Service: Service; + PositionState: CharacteristicValue; + TargetPosition: CharacteristicValue; + CurrentPosition: CharacteristicValue; + TargetHorizontalTiltAngle: CharacteristicValue; + CurrentHorizontalTiltAngle: CharacteristicValue; + }; + + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private LightSensor?: { + Service: Service; + CurrentAmbientLightLevel?: CharacteristicValue; + }; // OpenAPI Others - Mode!: string; - setPositionMode?: string | number; mappingMode: BlindTiltMappingMode = BlindTiltMappingMode.OnlyUp; - // BLE Status - BLE_InMotion: serviceData['inMotion']; - BLE_BatteryLevel: serviceData['battery']; - BLE_Calibration: serviceData['calibration']; - BLE_CurrentPosition: serviceData['position']; - BLE_CurrentAmbientLightLevel: serviceData['lightLevel']; - - // BLE Others - BLE_IsConnected?: boolean; - spaceBetweenLevels!: number; - // Target setNewTarget!: boolean; setNewTargetTimer!: NodeJS.Timeout; - //MQTT stuff - mqttClient: MqttClient | null = null; - - // Config - set_minStep!: number; - updateRate!: number; - set_minLux!: number; - set_maxLux!: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - setCloseMode!: string; - setOpenMode!: string; - maxRetries!: number; - delayBetweenRetries!: number; - // Updates blindTiltUpdateInProgress!: boolean; doBlindTiltUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + super(platform, accessory, device); // default placeholders - this.deviceLogs(device); - this.refreshRate(device); - this.scan(device); - this.setupMqtt(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - this.mappingMode = (device.blindTilt?.mode as BlindTiltMappingMode) ?? BlindTiltMappingMode.OnlyUp; this.debugLog(`Mapping mode: ${this.mappingMode}`); @@ -122,30 +63,43 @@ export class BlindTilt { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W2701600') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize LightBulb property + this.WindowCovering = { + Service: this.accessory.addService(this.hap.Service.WindowCovering), + PositionState: this.accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: this.accessory.context.TargetPosition || 100, + CurrentPosition: this.accessory.context.CurrentPosition || 100, + TargetHorizontalTiltAngle: this.accessory.context.TargetHorizontalTiltAngle || 0, + CurrentHorizontalTiltAngle: this.accessory.context.CurrentHorizontalTiltAngle || 0, + }; + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: this.accessory.context.BatteryLevel || 100, + StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; + + // Initialize LightSensor property + if (!this.device.blindTilt?.hide_lightsensor) { + this.LightSensor = { + Service: this.accessory.addService(this.hap.Service.LightSensor), + CurrentAmbientLightLevel: this.accessory.context.CurrentAmbientLightLevel || 0, + }; + } // get the WindowCovering service if it exists, otherwise create a new WindowCovering service // you can create multiple services for each accessory - const windowCoveringService = `${device.deviceName} ${device.deviceType}`; - (this.windowCoveringService = + (this.WindowCovering!.Service = accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService; + || accessory.addService(this.hap.Service.WindowCovering)), accessory.displayName; - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // create handlers for required characteristics - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState); + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); - this.windowCoveringService + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ minStep: this.minStep(device), @@ -154,10 +108,10 @@ export class BlindTilt { validValueRanges: [0, 100], }) .onGet(() => { - return this.CurrentPosition; + return this.WindowCovering?.CurrentPosition ?? 0; }); - this.windowCoveringService + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ minStep: this.minStep(device), @@ -167,8 +121,8 @@ export class BlindTilt { }) .onSet(this.TargetPositionSet.bind(this)); - this.CurrentHorizontalTiltAngle = 90; - this.windowCoveringService + this.WindowCovering!.CurrentHorizontalTiltAngle = 90; + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle) .setProps({ minStep: 180, @@ -177,12 +131,12 @@ export class BlindTilt { validValues: [-90, 90], }) .onGet(() => { - // this.debugLog(`requested CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`); - return this.CurrentHorizontalTiltAngle; + // this.debugLog(`requested CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); + return this.WindowCovering.CurrentHorizontalTiltAngle ?? 0; }); - this.TargetHorizontalTiltAngle = 90; - this.windowCoveringService + this.WindowCovering!.TargetHorizontalTiltAngle = 90; + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle) .setProps({ minStep: 180, @@ -194,13 +148,10 @@ export class BlindTilt { // Battery Service const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) || accessory.addService(this.hap.Service.Battery)), batteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } + this.Battery!.Service.setCharacteristic(this.hap.Characteristic.Name, batteryService); // Update Homekit this.updateHomeKitCharacteristics(); @@ -217,17 +168,17 @@ export class BlindTilt { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Please Submit Logs: ` + 'https://tinyurl.com/SwitchBotBug'); - /*const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; - this.updateHomeKitCharacteristics();*/ + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { slidePosition, battery } = context; + const { CurrentPosition } = this.WindowCovering; + const { BatteryLevel } = this.Battery; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(slidePosition, battery) = ' + + `Webhook:(${slidePosition}, ${battery}), ` + + `current:(${CurrentPosition}, ${BatteryLevel})`); + this.WindowCovering!.CurrentPosition = slidePosition; + this.Battery.BatteryLevel = battery; + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); @@ -236,13 +187,14 @@ export class BlindTilt { } // update slide progress - interval(this.updateRate * 1000) + interval(this.deviceUpdateRate * 1000) //.pipe(skipWhile(() => this.blindTiltUpdateInProgress)) .subscribe(async () => { - if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) { + if (this.WindowCovering!.PositionState === this.hap.Characteristic.PositionState.STOPPED) { return; } - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,` + + ` PositionState: ${this.WindowCovering!.PositionState}`); await this.refreshStatus(); }); @@ -270,218 +222,219 @@ export class BlindTilt { } /** - * Parse the device status from the SwitchBot api + * Parse the device status from the SwitchBotBLE API */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // CurrentPosition - this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition); + this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position); await this.setMinMax(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`); if (this.setNewTarget) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`); } - if (this.setNewTarget && this.BLE_InMotion) { + if (this.setNewTarget && serviceData.inMotion) { await this.setMinMax(); - if (Number(this.TargetPosition) > this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`); - } else if (Number(this.TargetPosition) < this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`); + if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); + } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby2, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby2,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` + + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`); - this.TargetPosition = this.CurrentPosition; - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + - ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`, + `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, ); if (!this.device.blindTilt?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - this.spaceBetweenLevels = 9; + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + const spaceBetweenLevels = 9; // Brightness - switch (this.BLE_CurrentAmbientLightLevel) { + switch (serviceData.lightLevel) { case 1: - this.CurrentAmbientLightLevel = this.set_minLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 2: - this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels; + this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`, ); break; case 3: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 4: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 5: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 6: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 7: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 8: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 9: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 10: default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; this.debugLog(); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`, + `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, ); } - async openAPIparseStatus(): Promise { + + /** + * Parse the device status from the SwitchBot OpenAPI + */ + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - const [homekitPosition, homekitTiltAngle] = this.mapDeviceValuesToHomekitValues(Number(this.OpenAPI_CurrentPosition), - String(this.OpenAPI_Direction)); - this.debugLog(` device: ${this.OpenAPI_CurrentPosition} => HK: ${homekitPosition}`); + const [homekitPosition, homekitTiltAngle] = this.mapDeviceValuesToHomekitValues(Number(deviceStatus.body.slidePosition), + String(deviceStatus.body.direction)); + this.debugLog(` device: ${deviceStatus.body.slidePosition} => HK: ${homekitPosition}`); - this.CurrentPosition = homekitPosition; + this.WindowCovering.CurrentPosition = homekitPosition; // CurrentPosition await this.setMinMax(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); if (homekitTiltAngle) { - this.CurrentHorizontalTiltAngle = homekitTiltAngle!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`); + this.WindowCovering.CurrentHorizontalTiltAngle = homekitTiltAngle!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); } if (this.setNewTarget) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`); } - if (this.setNewTarget && this.OpenAPI_InMotion) { + if (this.setNewTarget && deviceStatus.body.moving) { await this.setMinMax(); - if (this.TargetPosition > this.CurrentPosition || (homekitTiltAngle && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `); - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`); - } else if (this.TargetPosition < this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `); - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`); + if (this.WindowCovering.TargetPosition > this.WindowCovering.CurrentPosition + || (homekitTiltAngle && this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); + } else if (this.WindowCovering.TargetPosition < this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); } else { this.debugLog( - `${this.device.deviceType}: ${this.CurrentPosition} Standby because reached position,` + ` CurrentPosition: ${this.CurrentPosition}`, + `${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby because reached position,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, ); - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` + + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` + ` CurrentPosition: ${this.CurrentPosition}`, + `${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, ); - this.TargetPosition = this.CurrentPosition; + this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition; if (homekitTiltAngle) { - this.TargetHorizontalTiltAngle = this.CurrentHorizontalTiltAngle; + this.WindowCovering.TargetHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle; } - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + - ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`, + `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, ); if (!this.device.blindTilt?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); // Brightness - switch (this.OpenAPI_CurrentAmbientLightLevel) { + switch (deviceStatus.body.brightness) { case 'dim': - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; case 'bright': default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } async refreshStatus(): Promise { @@ -513,21 +466,13 @@ export class BlindTilt { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'x', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); - if (this.device.bleMac === ad.address && ad.model === 'x') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_InMotion = ad.serviceData.inMotion; - this.BLE_CurrentPosition = ad.serviceData.position; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } @@ -537,70 +482,19 @@ export class BlindTilt { // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'c', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_InMotion = ad.serviceData.inMotion; - this.BLE_CurrentPosition = ad.serviceData.position; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` + - `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` + - `inMotion: ${ad.serviceData.inMotion}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -608,13 +502,7 @@ export class BlindTilt { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentPosition = deviceStatus.body.slidePosition; - this.OpenAPI_Direction = deviceStatus.body.direction; - this.OpenAPI_InMotion = deviceStatus.body.moving; - this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -653,7 +541,7 @@ export class BlindTilt { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.TargetPosition !== this.CurrentPosition) { + if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) { const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -661,28 +549,24 @@ export class BlindTilt { .join(':') .toLowerCase(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - this.SilentPerformance(); - const adjustedMode = this.setPositionMode || null; - if (adjustedMode === null) { - this.Mode = 'Default Mode'; - } - this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`); + const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance(); + this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`); if (switchbot !== false) { switchbot .discover({ model: 'c', quick: true, id: this.device.bleMac }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - return await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode); + return await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), setPositionMode); }, }); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `Target Position: ${this.TargetPosition} sent over BLE, sent successfully`); + + `Target Position: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`); }) .catch(async (e: any) => { this.apiError(e); @@ -699,39 +583,24 @@ export class BlindTilt { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`, + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, ); } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - async openAPIpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); const hasDifferentAndRelevantHorizontalTiltAngle = - this.mappingMode === BlindTiltMappingMode.UseTiltForDirection && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle; - if (this.TargetPosition !== this.CurrentPosition || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) { - const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.TargetPosition), Number(this.TargetHorizontalTiltAngle)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition} (device = ${direction};${position})`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`); + this.mappingMode === BlindTiltMappingMode.UseTiltForDirection + && this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle; + if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition + || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) { + const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.WindowCovering.TargetPosition), + Number(this.WindowCovering.TargetHorizontalTiltAngle)); + const { Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` Pushing ${this.WindowCovering.TargetPosition} (device = ${direction};${position})`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`); let bodyChange = ''; if (position === 100) { bodyChange = JSON.stringify({ @@ -774,15 +643,16 @@ export class BlindTilt { } catch (e: any) { this.apiError(e); this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, + `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`, ); } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}` + - ` CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}, TargetPosition ${this.TargetHorizontalTiltAngle}`, + `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}` + + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle},` + + ` TargetPosition ${this.WindowCovering.TargetHorizontalTiltAngle}`, ); } } @@ -791,16 +661,16 @@ export class BlindTilt { * Handle requests to set the value of the "Target Horizontal Tilt" characteristic */ async TargetHorizontalTiltAngleSet(value: CharacteristicValue): Promise { - if (this.TargetHorizontalTiltAngle === this.accessory.context.TargetHorizontalTiltAngle) { + if (this.WindowCovering.TargetHorizontalTiltAngle === this.accessory.context.TargetHorizontalTiltAngle) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetHorizontalTiltAngle: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHorizontalTiltAngle: ${value}`); } //value = value < 0 ? -90 : 90; - this.TargetHorizontalTiltAngle = value; + this.WindowCovering.TargetHorizontalTiltAngle = value; if (this.device.mqttURL) { - this.mqttPublish('TargetHorizontalTiltAngle', this.TargetHorizontalTiltAngle); + this.mqttPublish('TargetHorizontalTiltAngle', this.WindowCovering.TargetHorizontalTiltAngle.toString()); } this.startUpdatingBlindTiltIfNeeded(); @@ -810,15 +680,15 @@ export class BlindTilt { * Handle requests to set the value of the "Target Position" characteristic */ async TargetPositionSet(value: CharacteristicValue): Promise { - if (this.TargetPosition === this.accessory.context.TargetPosition) { + if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); } - this.TargetPosition = value; + this.WindowCovering.TargetPosition = value; if (this.device.mqttURL) { - this.mqttPublish('TargetPosition', this.TargetPosition); + this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString()); } this.startUpdatingBlindTiltIfNeeded(); } @@ -826,39 +696,43 @@ export class BlindTilt { async startUpdatingBlindTiltIfNeeded(): Promise { await this.setMinMax(); this.debugLog('setMinMax'); - if (this.TargetPosition > this.CurrentPosition || this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle) { - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; + if (this.WindowCovering.TargetPosition > this.WindowCovering.CurrentPosition + || this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle) { + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; this.setNewTarget = true; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`, + `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, ); - } else if (this.TargetPosition < this.CurrentPosition) { - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; + } else if (this.WindowCovering.TargetPosition < this.WindowCovering.CurrentPosition) { + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; this.setNewTarget = true; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`, + `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, ); } else { - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.setNewTarget = false; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`, + `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, ); } - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState); - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); /** * If Blind Tilt movement time is short, the moving flag from backend is always false. * The minimum time depends on the network control latency. */ clearTimeout(this.setNewTargetTimer); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`); if (this.setNewTarget) { this.setNewTargetTimer = setTimeout(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`); this.setNewTarget = false; - }, this.updateRate * 1000); + }, this.deviceUpdateRate * 1000); } this.doBlindTiltUpdate.next(); } @@ -867,152 +741,97 @@ export class BlindTilt { await this.setMinMax(); // CurrentHorizontalTiltAngle if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) { - if (this.CurrentHorizontalTiltAngle === undefined || Number.isNaN(this.CurrentHorizontalTiltAngle)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`); + if (this.WindowCovering.CurrentHorizontalTiltAngle === undefined || Number.isNaN(this.WindowCovering.CurrentHorizontalTiltAngle)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); } else { if (this.device.mqttURL) { - this.mqttPublish('CurrentHorizontalTiltAngle', this.CurrentHorizontalTiltAngle); + this.mqttPublish('CurrentHorizontalTiltAngle', this.WindowCovering.CurrentHorizontalTiltAngle.toString()); } - this.accessory.context.CurrentHorizontalTiltAngle = this.CurrentHorizontalTiltAngle; - this.windowCoveringService.updateCharacteristic( + this.accessory.context.CurrentHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle; + this.WindowCovering.Service.updateCharacteristic( this.hap.Characteristic.CurrentHorizontalTiltAngle, - Number(this.CurrentHorizontalTiltAngle), + Number(this.WindowCovering.CurrentHorizontalTiltAngle), ); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} - updateCharacteristic CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`); + updateCharacteristic CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); } } // CurrentPosition - if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`); + if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else { if (this.device.mqttURL) { - this.mqttPublish('CurrentPosition', this.CurrentPosition); + this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); } - this.accessory.context.CurrentPosition = this.CurrentPosition; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.CurrentPosition)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`); + this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } // PositionState - if (this.PositionState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`); + if (this.WindowCovering.PositionState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`); } else { if (this.device.mqttURL) { - this.mqttPublish('PositionState', this.PositionState); + this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString()); } - this.accessory.context.PositionState = this.PositionState; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.PositionState)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`); + this.accessory.context.PositionState = this.WindowCovering.PositionState; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` PositionState: ${this.WindowCovering.PositionState}`); } // TargetPosition - if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`); + if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`); } else { if (this.device.mqttURL) { - this.mqttPublish('TargetPosition', this.TargetPosition); + this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString()); } - this.accessory.context.TargetPosition = this.TargetPosition; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.TargetPosition)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`); + this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}`); } // CurrentAmbientLightLevel if (!this.device.blindTilt?.hide_lightsensor) { - if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { if (this.device.mqttURL) { - this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel); + this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString()); } - this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel; - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } } // BatteryLevel - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { if (this.device.mqttURL) { - this.mqttPublish('BatteryLevel', this.BatteryLevel); + this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString()); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // StatusLowBattery - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { if (this.device.mqttURL) { - this.mqttPublish('StatusLowBattery', this.StatusLowBattery); - } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); - } - } - - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/blindtilt/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(topic: string, message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/blindtilt/${mac}/${topic}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString()); } - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'c', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } @@ -1032,221 +851,104 @@ export class BlindTilt { } } - async SilentPerformance() { - if (Number(this.TargetPosition) > 50) { + async setPerformance() { + let setPositionMode: number; + let Mode: string; + if (Number(this.WindowCovering.TargetPosition) > 50) { if (this.device.blindTilt?.setOpenMode === '1') { - this.setPositionMode = 1; - this.Mode = 'Silent Mode'; + setPositionMode = 1; + Mode = 'Silent Mode'; + } else if (this.device.blindTilt?.setOpenMode === '0') { + setPositionMode = 0; + Mode = 'Performance Mode'; } else { - this.setPositionMode = 0; - this.Mode = 'Performance Mode'; + setPositionMode = 0; + Mode = 'Default Mode'; } } else { if (this.device.blindTilt?.setCloseMode === '1') { - this.setPositionMode = 1; - this.Mode = 'Silent Mode'; + setPositionMode = 1; + Mode = 'Silent Mode'; + } else if (this.device.blindTilt?.setOpenMode === '0') { + setPositionMode = 0; + Mode = 'Performance Mode'; } else { - this.setPositionMode = 0; - this.Mode = 'Performance Mode'; + setPositionMode = 0; + Mode = 'Default Mode'; } } + return { setPositionMode, Mode }; } async setMinMax(): Promise { if (this.device.blindTilt?.set_min) { - if (Number(this.CurrentPosition) <= this.device.blindTilt?.set_min) { - this.CurrentPosition = 0; + if (Number(this.WindowCovering.CurrentPosition) <= this.device.blindTilt?.set_min) { + this.WindowCovering.CurrentPosition = 0; } } if (this.device.blindTilt?.set_max) { - if (Number(this.CurrentPosition) >= this.device.blindTilt?.set_max) { - this.CurrentPosition = 100; + if (Number(this.WindowCovering.CurrentPosition) >= this.device.blindTilt?.set_max) { + this.WindowCovering.CurrentPosition = 100; } } if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) { - this.CurrentHorizontalTiltAngle = Number(this.CurrentHorizontalTiltAngle) < 0 ? -90 : 90; + this.WindowCovering.CurrentHorizontalTiltAngle = Number(this.WindowCovering.CurrentHorizontalTiltAngle) < 0 ? -90 : 90; } } minStep(device: device & devicesConfig): number { + let set_minStep: number; if (device.blindTilt?.set_minStep) { - this.set_minStep = device.blindTilt?.set_minStep; + set_minStep = device.blindTilt?.set_minStep; } else { - this.set_minStep = 1; + set_minStep = 1; } - return this.set_minStep; + return set_minStep; } - minLux(): number { + async minLux(): Promise { + let set_minLux: number; if (this.device.blindTilt?.set_minLux) { - this.set_minLux = this.device.blindTilt?.set_minLux; + set_minLux = this.device.blindTilt?.set_minLux; } else { - this.set_minLux = 1; + set_minLux = 1; } - return this.set_minLux; + return set_minLux; } - maxLux(): number { + async maxLux(): Promise { + let set_maxLux: number; if (this.device.blindTilt?.set_maxLux) { - this.set_maxLux = this.device.blindTilt?.set_maxLux; + set_maxLux = this.device.blindTilt?.set_maxLux; } else { - this.set_maxLux = 6001; - } - return this.set_maxLux; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - if (this.updateRate > device.scanDuration) { - this.scanDuration = this.updateRate; - if (this.BLE) { - this.warnLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`, - ); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - } - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - if (this.updateRate > 1) { - this.scanDuration = this.updateRate; - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - } - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_maxLux = 6001; } + return set_maxLux; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); } } async apiError(e: any): Promise { - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, e); - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); if (!this.device.curtain?.hide_lightsensor) { - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); } if (this.BLE) { - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); } - async deviceContext() { - if (this.CurrentPosition === undefined) { - this.CurrentPosition = 0; - } else { - this.CurrentPosition = this.accessory.context.CurrentPosition; - } - - if (this.TargetPosition === undefined) { - this.TargetPosition = 0; - } else { - this.TargetPosition = this.accessory.context.TargetPosition; - } - - if (this.PositionState === undefined) { - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - } else { - this.PositionState = this.accessory.context.PositionState; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - /** * Maps device values to homekit values * @@ -1366,159 +1068,4 @@ export class BlindTilt { } } } - - async refreshRate(device: device & devicesConfig): Promise { - // refreshRate - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - // updateRate - if (device?.blindTilt?.updateRate) { - this.updateRate = device?.blindTilt?.updateRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Blind Tilt updateRate: ${this.updateRate}`); - } else { - this.updateRate = 2; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Blind Tilt updateRate: ${this.updateRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.blindTilt) { - config = device.blindTilt; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (device.maxRetries !== undefined) { - config['maxRetries'] = device.maxRetries; - } - if (device.delayBetweenRetries !== undefined) { - config['delayBetweenRetries'] = device.delayBetweenRetries; - } - if (device.blindTilt?.mode === undefined) { - config['mode'] = BlindTiltMappingMode.OnlyUp; - } else { - config['mode'] = device.blindTilt?.mode; - } - - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/device/bot.ts b/src/device/bot.ts index 661f437e..d9e5c8a3 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -1,99 +1,98 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * bot.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Bot { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Bot extends deviceBase { // Services - fanService?: Service; - doorService?: Service; - lockService?: Service; - faucetService?: Service; - windowService?: Service; - switchService?: Service; - outletService?: Service; - batteryService: Service; - garageDoorService?: Service; - windowCoveringService?: Service; - statefulProgrammableSwitchService?: Service; - - // Characteristic Values - On!: CharacteristicValue; - BatteryLevel!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_On: deviceStatus['power']; - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - - // BLE Status - BLE_On!: serviceData['state']; - BLE_Mode!: serviceData['mode']; - BLE_BatteryLevel!: serviceData['battery']; - - //BLE Others - BLE_IsConnected?: boolean; + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private Switch?: { + Service: Service; + On: CharacteristicValue; + }; + + private GarageDoor?: { + Service: Service; + On: CharacteristicValue; + }; + + private Door?: { + Service: Service; + On: CharacteristicValue; + }; + + private Window?: { + Service: Service; + On: CharacteristicValue; + }; + + private WindowCovering?: { + Service: Service; + On: CharacteristicValue; + }; + + private Lock?: { + Service: Service; + On: CharacteristicValue; + }; + + private Faucet?: { + Service: Service; + On: CharacteristicValue; + }; + + private Fan?: { + Service: Service; + On: CharacteristicValue; + }; + + private StatefulProgrammableSwitch?: { + Service: Service; + On: CharacteristicValue; + }; + + private Outlet?: { + Service: Service; + On: CharacteristicValue; + }; // Config botMode!: string; allowPush?: boolean; doublePress!: number; - scanDuration!: number; botDeviceType!: string; pushRatePress!: number; - deviceLogging!: string; multiPressCount!: number; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; // Updates botUpdateInProgress!: boolean; doBotUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + super(platform, accessory, device); // default placeholders - this.deviceLogs(device); - this.deviceType(device); - this.scan(device); - this.refreshRate(device); - this.PressOrSwitch(device); - this.allowPushChanges(device); - this.deviceContext(); - this.DoublePress(device); - this.deviceRetry(device); - this.deviceConfig(device); - - this.multiPressCount = 0; + this.getBotConfigSettings(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doBotUpdate = new Subject(); @@ -102,16 +101,20 @@ export class Bot { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'SWITCHBOT-BOT-S1') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Battery property + this.Battery = { + Service: accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; // deviceType if (this.botDeviceType === 'switch') { + // Initialize Switch property + this.Switch = { + Service: this.accessory.addService(this.hap.Service.Switch), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -124,16 +127,18 @@ export class Bot { // Add switchService const switchService = `${accessory.displayName} Switch`; - (this.switchService = accessory.getService(this.hap.Service.Switch) + (this.Switch!.Service = accessory.getService(this.hap.Service.Switch) || accessory.addService(this.hap.Service.Switch)), switchService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`); - this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.switchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.switchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'garagedoor') { + // Initialize Switch property + this.GarageDoor = { + Service: this.accessory.addService(this.hap.Service.GarageDoorOpener), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -146,17 +151,19 @@ export class Bot { // Add garageDoorService const garageDoorService = `${accessory.displayName} Garage Door Opener`; - (this.garageDoorService = accessory.getService(this.hap.Service.GarageDoorOpener) + (this.GarageDoor!.Service = accessory.getService(this.hap.Service.GarageDoorOpener) || accessory.addService(this.hap.Service.GarageDoorOpener)), garageDoorService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`); - this.garageDoorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.garageDoorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.garageDoorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.garageDoorService.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); - this.garageDoorService.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); + this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.GarageDoor!.Service.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); + this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); } else if (this.botDeviceType === 'door') { + // Initialize Switch property + this.Door = { + Service: this.accessory.addService(this.hap.Service.Door), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeOutletService(accessory); @@ -169,15 +176,12 @@ export class Bot { // Add doorService const doorService = `${accessory.displayName} Door`; - (this.doorService = accessory.getService(this.hap.Service.Door) + (this.Door!.Service = accessory.getService(this.hap.Service.Door) || accessory.addService(this.hap.Service.Door)), doorService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`); - this.doorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.doorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.doorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.doorService + this.Door!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Door!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -186,8 +190,13 @@ export class Bot { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.doorService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Door!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.botDeviceType === 'window') { + // Initialize Switch property + this.Window = { + Service: this.accessory.addService(this.hap.Service.Window), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -200,15 +209,12 @@ export class Bot { // Add windowService const windowService = `${accessory.displayName} Window`; - (this.windowService = accessory.getService(this.hap.Service.Window) + (this.Window!.Service = accessory.getService(this.hap.Service.Window) || accessory.addService(this.hap.Service.Window)), windowService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`); - this.windowService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.windowService + this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Window!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -217,8 +223,13 @@ export class Bot { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.windowService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Window!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.botDeviceType === 'windowcovering') { + // Initialize Switch property + this.WindowCovering = { + Service: this.accessory.addService(this.hap.Service.WindowCovering), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -231,15 +242,12 @@ export class Bot { // Add windowCoveringService const windowCoveringService = `${accessory.displayName} Window Covering`; - (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering) + (this.WindowCovering!.Service = accessory.getService(this.hap.Service.WindowCovering) || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`); - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.windowCoveringService + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -248,8 +256,13 @@ export class Bot { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.botDeviceType === 'lock') { + // Initialize Switch property + this.Lock = { + Service: this.accessory.addService(this.hap.Service.LockMechanism), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeDoorService(accessory); this.removeOutletService(accessory); @@ -262,16 +275,18 @@ export class Bot { // Add lockService const lockService = `${accessory.displayName} Lock`; - (this.lockService = accessory.getService(this.hap.Service.LockMechanism) + (this.Lock!.Service = accessory.getService(this.hap.Service.LockMechanism) || accessory.addService(this.hap.Service.LockMechanism)), lockService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`); - this.lockService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lockService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lockService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.lockService.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); + this.Lock!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Lock!.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'faucet') { + // Initialize Switch property + this.Faucet = { + Service: this.accessory.addService(this.hap.Service.Faucet), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -284,16 +299,18 @@ export class Bot { // Add faucetService const faucetService = `${accessory.displayName} Faucet`; - (this.faucetService = accessory.getService(this.hap.Service.Faucet) + (this.Faucet!.Service = accessory.getService(this.hap.Service.Faucet) || accessory.addService(this.hap.Service.Faucet)), faucetService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`); - this.faucetService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.faucetService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.faucetService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.faucetService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); + this.Faucet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Faucet!.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'fan') { + // Initialize Switch property + this.Fan = { + Service: this.accessory.addService(this.hap.Service.Fanv2), + On: accessory.context.On || false, + }; this.removeLockService(accessory); this.removeDoorService(accessory); this.removeFaucetService(accessory); @@ -306,16 +323,18 @@ export class Bot { // Add fanService const fanService = `${accessory.displayName} Fan`; - (this.fanService = accessory.getService(this.hap.Service.Fan) - || accessory.addService(this.hap.Service.Fan)), fanService; + (this.Fan!.Service = accessory.getService(this.hap.Service.Fanv2) + || accessory.addService(this.hap.Service.Fanv2)), fanService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`); - this.fanService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.fanService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.fanService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.fanService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Fan!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Fan!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'stateful') { + // Initialize Switch property + this.StatefulProgrammableSwitch = { + Service: this.accessory.addService(this.hap.Service.StatefulProgrammableSwitch), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -328,18 +347,20 @@ export class Bot { // Add statefulProgrammableSwitchService const statefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`; - (this.statefulProgrammableSwitchService = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) || + (this.StatefulProgrammableSwitch!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), statefulProgrammableSwitchService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); - this.statefulProgrammableSwitchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.statefulProgrammableSwitchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.statefulProgrammableSwitchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.statefulProgrammableSwitchService + this.StatefulProgrammableSwitch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.StatefulProgrammableSwitch!.Service .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) .onSet(this.OnSet.bind(this)); } else { + // Initialize Switch property + this.Outlet = { + Service: this.accessory.addService(this.hap.Service.Outlet), + On: accessory.context.On || false, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -352,27 +373,21 @@ export class Bot { // Add outletService const outletService = `${accessory.displayName} Outlet`; - (this.outletService = accessory.getService(this.hap.Service.Outlet) + (this.Outlet!.Service = accessory.getService(this.hap.Service.Outlet) || accessory.addService(this.hap.Service.Outlet)), outletService; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`); - this.outletService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.outletService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.outletService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.outletService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Outlet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } // batteryService const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) || accessory.addService(this.hap.Service.Battery)), batteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -391,13 +406,15 @@ export class Bot { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { power, battery, deviceMode } = context; - const { On, BatteryLevel, botMode } = this; + const { botMode } = this; + const On = await this.getOn(); + const { BatteryLevel } = this.Battery; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(power, battery, deviceMode) = ' + `Webhook:(${power}, ${battery}, ${deviceMode}), ` + `current:(${On}, ${BatteryLevel}, ${botMode})`); - this.On = power; - this.BatteryLevel = battery; + await this.setOn(power); + this.Battery.BatteryLevel = battery; this.botMode = deviceMode; this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -439,80 +456,70 @@ export class Bot { } /** - * Parse the device status from the SwitchBot api + * Parse the device status from the SwitchBotBLE API */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // BLEmode (true if Switch Mode) | (false if Press Mode) - if (this.BLE_Mode) { - this.accessory.context.On = this.On; - if (this.On === undefined) { - this.On = Boolean(this.BLE_On); + if (serviceData.mode) { + this.accessory.context.On = await this.getOn(); + if (this.getOn() === undefined) { + this.setOn(Boolean(serviceData.state)); } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, mode: ${this.BLE_Mode}, On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode,` + + ` mode: ${serviceData.mode}, On: ${this.accessory.context.On}`); } else { - this.On = false; - this.accessory.context.On = this.On; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode, mode: ${this.BLE_Mode}, On: ${this.On}`); + this.setOn(false); + this.accessory.context.On = await this.getOn(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode,` + + ` mode: ${serviceData.mode}, On: ${this.accessory.context.On}`); } - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } - async openAPIparseStatus(): Promise { + + /** + * Parse the device status from the SwitchBot OpenAPI + */ + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); if (this.botMode === 'press') { - this.On = false; - this.accessory.context.On = this.On; + this.setOn(false); + this.accessory.context.On = await this.getOn(); } else { - this.accessory.context.On = this.On; - if (this.On === undefined) { - this.On = false; + this.accessory.context.On = await this.getOn(); + if (this.getOn() === undefined) { + this.setOn(false); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.accessory.context.On}`); // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -547,89 +554,35 @@ export class Bot { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'H', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); - if (this.device.bleMac === ad.address && ad.model === 'H') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_Mode = ad.serviceData.mode; - this.BLE_On = ad.serviceData.state; - this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'H', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_Mode = ad.serviceData.mode; - this.BLE_On = ad.serviceData.state; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}, model: ${ad.serviceData.model}, modelName: ` + - `${ad.serviceData.modelName}, mode: ${ad.serviceData.mode}, state: ${ad.serviceData.state}, battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -637,10 +590,7 @@ export class Bot { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -686,8 +636,9 @@ export class Bot { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On || this.allowPush) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + const On = await this.getOn(); + if (On !== this.accessory.context.On || this.allowPush) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -701,21 +652,21 @@ export class Bot { switchbot .discover({ model: 'H', quick: true, id: this.device.bleMac }) .then(async (device_list: { press: (arg0: { id: string | undefined }) => any }[]) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}`); return await device_list[0].press({ id: this.device.bleMac }); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.accessory.context.On = this.On; + + `On: ${On} sent over BLE, sent successfully`); + this.accessory.context.On = On; setTimeout(() => { if (this.botDeviceType === 'switch') { - this.switchService?.getCharacteristic(this.hap.Characteristic.On).updateValue(this.On); + this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); } else { - this.outletService?.getCharacteristic(this.hap.Characteristic.On).updateValue(this.On); + this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}, Switch Timeout`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}, Switch Timeout`); }, 500); }) .catch(async (e: any) => { @@ -731,11 +682,11 @@ export class Bot { switchbot .discover({ model: 'H', quick: true, id: this.device.bleMac }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -745,46 +696,43 @@ export class Bot { }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.accessory.context.On = this.On; + this.accessory.context.On = On; }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, On: ${On}, OnCached: ${this.accessory.context.On}`); } } async openAPIpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); + let On = await this.getOn(); if (this.multiPressCount > 0) { this.debugLog(`${this.device.deviceType}: ${this.multiPressCount} request(s) queued.`); - this.On = true; + On = true; } - if (this.On !== this.accessory.context.On || this.allowPush || this.multiPressCount > 0) { + if (On !== this.accessory.context.On || this.allowPush || this.multiPressCount > 0) { let command = ''; - if (this.botMode === 'switch' && this.On) { + if (this.botMode === 'switch' && On) { command = 'turnOn'; - this.On = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); - } else if (this.botMode === 'switch' && !this.On) { + On = true; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${On}`); + } else if (this.botMode === 'switch' && !On) { command = 'turnOff'; - this.On = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`); + On = false; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${On}`); } else if (this.botMode === 'press' || this.botMode === 'multipress') { command = 'press'; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode`); - this.On = false; + On = false; } else { throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Device Parameters not set for this Bot.`); } @@ -818,23 +766,18 @@ export class Bot { this.multiPressCount--; if (this.multiPressCount > 0) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Multi Press Count: ${this.multiPressCount}`); - this.On = true; + On = true; this.openAPIpushChanges(); } } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges,` + + ` On: ${On}, OnCached: ${this.accessory.context.On}`); } } @@ -845,51 +788,85 @@ export class Bot { if (this.botDeviceType === 'garagedoor') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`); if (value === this.hap.Characteristic.TargetDoorState.CLOSED) { - this.On = false; + await this.setOn(false); + this.GarageDoor!.On = false; + } else { + await this.setOn(true); + this.GarageDoor!.On = true; + } + } else if (this.botDeviceType === 'door') { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Door!.On = false; + } else { + await this.setOn(true); + this.Door!.On = true; + } + } else if (this.botDeviceType === 'window') { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Window!.On = false; } else { - this.On = true; + await this.setOn(true); + this.Window!.On = true; } - } else if ( - this.botDeviceType === 'door' || - this.botDeviceType === 'window' || - this.botDeviceType === 'windowcovering' - ) { + } else if (this.botDeviceType === 'windowcovering') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); if (value === 0) { - this.On = false; + await this.setOn(false); + this.WindowCovering!.On = false; } else { - this.On = true; + await this.setOn(true); + this.WindowCovering!.On = true; } } else if (this.botDeviceType === 'lock') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); if (value === this.hap.Characteristic.LockTargetState.SECURED) { - this.On = false; + await this.setOn(false); + this.Lock!.On = false; } else { - this.On = true; + await this.setOn(true); + this.Lock!.On = true; } } else if (this.botDeviceType === 'faucet') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); if (value === this.hap.Characteristic.Active.INACTIVE) { - this.On = false; + await this.setOn(false); + this.Faucet!.On = false; } else { - this.On = true; + await this.setOn(true); + this.Faucet!.On = true; } } else if (this.botDeviceType === 'stateful') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); if (value === 0) { - this.On = false; + await this.setOn(false); + this.StatefulProgrammableSwitch!.On = false; } else { - this.On = true; + await this.setOn(true); + this.StatefulProgrammableSwitch!.On = true; + } + } else if (this.botDeviceType === 'switch') { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Switch!.On = false; + } else { + await this.setOn(true); + this.Switch!.On = true; } } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); - if (this.device.bot?.mode === 'multipress') { - if (value === true) { - this.multiPressCount++; - this.debugLog(`${this.device.deviceType} set to Multi-Press. Multi-Press count: ${this.multiPressCount}`); - } + await this.setOn(Boolean(value)); + this.Outlet!.On = value; + } + if (this.device.bot?.mode === 'multipress') { + if (value === true) { + this.multiPressCount++; + this.debugLog(`${this.device.deviceType} set to Multi-Press. Multi-Press count: ${this.multiPressCount}`); } - this.On = value; } this.doBotUpdate.next(); } @@ -900,536 +877,328 @@ export class Bot { async updateHomeKitCharacteristics(): Promise { // State if (this.botDeviceType === 'garagedoor') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.GarageDoor!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.GarageDoor!.On}`); } else { - if (this.On) { - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.TargetDoorState, - this.hap.Characteristic.TargetDoorState.OPEN, - ); - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.CurrentDoorState, - this.hap.Characteristic.CurrentDoorState.OPEN, - ); - this.debugLog( - `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`, - ); + if (this.GarageDoor!.On) { + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); } else { - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.TargetDoorState, - this.hap.Characteristic.TargetDoorState.CLOSED, - ); - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.CurrentDoorState, - this.hap.Characteristic.CurrentDoorState.CLOSED, - ); - this.debugLog( - `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`, - ); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.On}`); + await this.setOn(Boolean(this.GarageDoor!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor!.On}`); } else if (this.botDeviceType === 'door') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Door!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Door!.On}`); } else { - if (this.On) { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.Door!.On) { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door!.On})`); } else { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.On}`); + await this.setOn(Boolean(this.Door!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.Door!.On}`); } else if (this.botDeviceType === 'window') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Window!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Window!.On}`); } else { - if (this.On) { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.Window!.On) { + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window!.On})`); } else { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.On}`); + await this.setOn(Boolean(this.Window!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.Window!.On}`); } else if (this.botDeviceType === 'windowcovering') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.WindowCovering!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.WindowCovering!.On}`); } else { - if (this.On) { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.windowCoveringService?.updateCharacteristic( - this.hap.Characteristic.PositionState, - this.hap.Characteristic.PositionState.STOPPED, - ); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.WindowCovering!.On) { + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering!.On})`); } else { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.windowCoveringService?.updateCharacteristic( - this.hap.Characteristic.PositionState, - this.hap.Characteristic.PositionState.STOPPED, - ); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.On}`); + await this.setOn(Boolean(this.WindowCovering!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); } else if (this.botDeviceType === 'lock') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Lock!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Lock!.On}`); } else { - if (this.On) { - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockTargetState, - this.hap.Characteristic.LockTargetState.UNSECURED, - ); - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockCurrentState, - this.hap.Characteristic.LockCurrentState.UNSECURED, - ); - this.debugLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} updateCharacteristic LockTargetState: UNSECURED, LockCurrentState: UNSECURED`, - ); + if (this.Lock!.On) { + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.Lock!.On})`); } else { - this.lockService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockCurrentState, - this.hap.Characteristic.LockCurrentState.SECURED, - ); - this.debugLog( - `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic LockTargetState: SECURED, LockCurrentState: SECURED`, - ); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.Lock!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.On}`); + await this.setOn(Boolean(this.Lock!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.Lock!.On}`); } else if (this.botDeviceType === 'faucet') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Faucet!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Faucet!.On}`); } else { - if (this.On) { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + if (this.Faucet!.On) { + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); } else { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.On}`); + await this.setOn(Boolean(this.Faucet!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.Faucet!.On}`); } else if (this.botDeviceType === 'fan') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Fan!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan!.On}`); } else { - if (this.On) { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + if (this.Fan!.On) { + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); } else { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.On}`); + await this.setOn(Boolean(this.Fan!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.Fan!.On}`); } else if (this.botDeviceType === 'stateful') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.StatefulProgrammableSwitch!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.StatefulProgrammableSwitch!.On}`); } else { - if (this.On) { - this.statefulProgrammableSwitchService?.updateCharacteristic( - this.hap.Characteristic.ProgrammableSwitchEvent, - this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, - ); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); - this.debugLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1`, - ); + if (this.StatefulProgrammableSwitch!.On) { + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch!.On})`); } else { - this.statefulProgrammableSwitchService?.updateCharacteristic( - this.hap.Characteristic.ProgrammableSwitchEvent, - this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, - ); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); - this.debugLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0`, - ); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch!.On})`); } } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.On}`); + await this.setOn(Boolean(this.StatefulProgrammableSwitch!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch!.On}`); } else if (this.botDeviceType === 'switch') { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Switch!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Switch!.On}`); } else { - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch!.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch!.On}`); + await this.setOn(Boolean(this.Switch!.On)); } } else { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Outlet!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet!.On}`); } else { - this.outletService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet!.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet!.On}`); } } - this.accessory.context.On = this.On; + await this.setOn(Boolean(this.Outlet!.On)); + this.accessory.context.On = await this.getOn(); // BatteryLevel - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // StatusLowBattery - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } async removeOutletService(accessory: PlatformAccessory): Promise { // If outletService still present, then remove first - this.outletService = this.accessory.getService(this.hap.Service.Outlet); - if (this.outletService) { + this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + if (this.Outlet?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); } - accessory.removeService(this.outletService!); + accessory.removeService(this.Outlet!.Service); } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If garageDoorService still present, then remove first - this.garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener); - if (this.garageDoorService) { + this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + if (this.GarageDoor?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); } - accessory.removeService(this.garageDoorService!); + accessory.removeService(this.GarageDoor!.Service); } async removeDoorService(accessory: PlatformAccessory): Promise { // If doorService still present, then remove first - this.doorService = this.accessory.getService(this.hap.Service.Door); - if (this.doorService) { + this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + if (this.Door?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); } - accessory.removeService(this.doorService!); + accessory.removeService(this.Door!.Service); } async removeLockService(accessory: PlatformAccessory): Promise { // If lockService still present, then remove first - this.lockService = this.accessory.getService(this.hap.Service.LockMechanism); - if (this.lockService) { + this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + if (this.Lock?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); } - accessory.removeService(this.lockService!); + accessory.removeService(this.Lock!.Service); } async removeFaucetService(accessory: PlatformAccessory): Promise { // If faucetService still present, then remove first - this.faucetService = this.accessory.getService(this.hap.Service.Faucet); - if (this.faucetService) { + this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; + if (this.Faucet?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Faucet Service`); } - accessory.removeService(this.faucetService!); + accessory.removeService(this.Faucet!.Service); } async removeFanService(accessory: PlatformAccessory): Promise { // If fanService still present, then remove first - this.fanService = this.accessory.getService(this.hap.Service.Fan); - if (this.fanService) { + this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; + if (this.Fan?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); } - accessory.removeService(this.fanService!); + accessory.removeService(this.Fan!.Service); } async removeWindowService(accessory: PlatformAccessory): Promise { // If windowService still present, then remove first - this.windowService = this.accessory.getService(this.hap.Service.Window); - if (this.windowService) { + this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + if (this.Window?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); } - accessory.removeService(this.windowService!); + accessory.removeService(this.Window!.Service); } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If windowCoveringService still present, then remove first - this.windowCoveringService = this.accessory.getService(this.hap.Service.WindowCovering); - if (this.windowCoveringService) { + this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + if (this.WindowCovering?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); } - accessory.removeService(this.windowCoveringService!); + accessory.removeService(this.WindowCovering!.Service); } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If statefulProgrammableSwitchService still present, then remove first - this.statefulProgrammableSwitchService = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch); - if (this.statefulProgrammableSwitchService) { + this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; + if (this.StatefulProgrammableSwitch?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); } - accessory.removeService(this.statefulProgrammableSwitchService!); + accessory.removeService(this.StatefulProgrammableSwitch!.Service); } async removeSwitchService(accessory: PlatformAccessory): Promise { // If switchService still present, then remove first - this.switchService = this.accessory.getService(this.hap.Service.Switch); - if (this.switchService) { + this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + if (this.Switch?.Service) { this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); } - accessory.removeService(this.switchService!); - } - - private DoublePress(device: device & devicesConfig) { - if (device.bot?.doublePress) { - this.doublePress = device.bot?.doublePress; - this.accessory.context.doublePress = this.doublePress; - } else { - this.doublePress = 1; - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'H', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - - async BLEPushConnection() { - if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); - await this.openAPIpushChanges(); - } - } - - async BLERefreshConnection(switchbot: any): Promise { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` - + ` ${JSON.stringify(switchbot)}`); - if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`); - await this.openAPIRefreshStatus(); - } - } - - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - - async PressOrSwitch(device: device & devicesConfig): Promise { - if (!device.bot?.mode) { - this.botMode = 'switch'; - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} does not have bot mode set in the Plugin's SwitchBot Device Settings,`); - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} is defaulting to "${this.botMode}" mode, you may experience issues.`); - } else if (device.bot?.mode === 'switch') { - this.botMode = 'switch'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); - } else if (device.bot?.mode === 'press') { - this.botMode = 'press'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); - } else if (device.bot?.mode === 'multipress') { - this.botMode = 'multipress'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); - } else { - throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); - } + accessory.removeService(this.Switch!.Service); } - async allowPushChanges(device: device & devicesConfig): Promise { - if (device.bot?.allowPush) { - this.allowPush = true; - } else { - this.allowPush = false; - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Allowing Push Changes: ${this.allowPush}`); - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } + async getOn(): Promise { + let On: boolean; + if (this.botDeviceType === 'garagedoor') { + On = this.GarageDoor!.On ? true : false; + } else if (this.botDeviceType === 'door') { + On = this.Door!.On ? true : false; + } else if (this.botDeviceType === 'window') { + On = this.Window!.On ? true : false; + } else if (this.botDeviceType === 'windowcovering') { + On = this.WindowCovering!.On ? true : false; + } else if (this.botDeviceType === 'lock') { + On = this.Lock!.On ? true : false; + } else if (this.botDeviceType === 'faucet') { + On = this.Faucet!.On ? true : false; + } else if (this.botDeviceType === 'fan') { + On = this.Fan!.On ? true : false; + } else if (this.botDeviceType === 'stateful') { + On = this.StatefulProgrammableSwitch!.On ? true : false; + } else if (this.botDeviceType === 'switch') { + On = this.Switch!.On ? true : false; } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + On = this.Outlet!.On ? true : false; } + return On; } - async offlineOff(): Promise { - if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); - } - } - - async apiError(e: any): Promise { + async setOn(On: boolean): Promise { if (this.botDeviceType === 'garagedoor') { - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + this.GarageDoor!.On = On; } else if (this.botDeviceType === 'door') { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.Door!.On = On; } else if (this.botDeviceType === 'window') { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.Window!.On = On; } else if (this.botDeviceType === 'windowcovering') { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.WindowCovering!.On = On; } else if (this.botDeviceType === 'lock') { - this.doorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + this.Lock!.On = On; } else if (this.botDeviceType === 'faucet') { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, e); + this.Faucet!.On = On; } else if (this.botDeviceType === 'fan') { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Fan!.On = On; } else if (this.botDeviceType === 'stateful') { - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + this.StatefulProgrammableSwitch!.On = On; } else if (this.botDeviceType === 'switch') { - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Switch!.On = On; } else { - this.outletService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Outlet!.On = On; } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } - async deviceType(device: device & devicesConfig): Promise { + async getBotConfigSettings(device: device & devicesConfig) { + //Bot Device Type if (!device.bot?.deviceType && this.accessory.context.deviceType) { this.botDeviceType = this.accessory.context.deviceType; this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Type: ${this.botDeviceType}, from Accessory Cache.`); @@ -1443,41 +1212,33 @@ export class Bot { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} No Device Type Set, deviceType: ${this.device.bot?.deviceType}`); this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using default deviceType: ${this.botDeviceType}`); } - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - this.accessory.context.On = this.On; - } else { - this.On = this.accessory.context.On; - } - if (this.BatteryLevel === undefined) { - this.BatteryLevel = 100; + // Bot Mode + if (!device.bot?.mode) { + this.botMode = 'switch'; + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} does not have bot mode set in the Plugin's SwitchBot Device Settings,`); + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} is defaulting to "${this.botMode}" mode, you may experience issues.`); + } else if (device.bot?.mode === 'switch') { + this.botMode = 'switch'; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); + } else if (device.bot?.mode === 'press') { + this.botMode = 'press'; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); + } else if (device.bot?.mode === 'multipress') { + this.botMode = 'multipress'; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`); } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; + throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); } - if (this.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - this.accessory.context.StatusLowBattery = this.StatusLowBattery; + + // Bot Double Press + if (device.bot?.doublePress) { + this.doublePress = device.bot?.doublePress; + this.accessory.context.doublePress = this.doublePress; } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.doublePress = 1; } - } - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - // pushRatePress + // Bot Press PushRate if (device?.bot?.pushRatePress) { this.pushRatePress = device?.bot?.pushRatePress; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Bot pushRatePress: ${this.pushRatePress}`); @@ -1485,129 +1246,104 @@ export class Bot { this.pushRatePress = 15; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Bot pushRatePress: ${this.pushRatePress}`); } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.bot) { - config = device.bot; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); + // Bot Allow Push + if (device.bot?.allowPush) { + this.allowPush = true; } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } + this.allowPush = false; } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Allowing Push Changes: ${this.allowPush}`); + // Bot Multi Press Count + this.multiPressCount = 0; } - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); + async BLEPushConnection() { + if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); + await this.openAPIpushChanges(); } } - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } + async BLERefreshConnection(switchbot: any): Promise { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + + ` ${JSON.stringify(switchbot)}`); + if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`); + await this.openAPIRefreshStatus(); } } - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); + async offlineOff(): Promise { + if (this.device.offline) { + if (this.botDeviceType === 'garagedoor') { + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, false); + } else if (this.botDeviceType === 'door') { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } else if (this.botDeviceType === 'window') { + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } else if (this.botDeviceType === 'windowcovering') { + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } else if (this.botDeviceType === 'lock') { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + } else if (this.botDeviceType === 'faucet') { + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + } else if (this.botDeviceType === 'fan') { + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, false); + } else if (this.botDeviceType === 'stateful') { + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + } else if (this.botDeviceType === 'switch') { + this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, false); } else { - this.platform.log.debug(String(...log)); + this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } } - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + async apiError(e: any): Promise { + if (this.botDeviceType === 'garagedoor') { + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + } else if (this.botDeviceType === 'door') { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } else if (this.botDeviceType === 'window') { + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } else if (this.botDeviceType === 'windowcovering') { + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } else if (this.botDeviceType === 'lock') { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + } else if (this.botDeviceType === 'faucet') { + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + } else if (this.botDeviceType === 'fan') { + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } else if (this.botDeviceType === 'stateful') { + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + } else if (this.botDeviceType === 'switch') { + this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } else { + this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 15f5afb8..142ab2b6 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -1,94 +1,48 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * ceilinglight.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; +import { deviceBase } from './device.js'; +import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; -import { - Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, -} from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class CeilingLight { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class CeilingLight extends deviceBase { // Services - lightBulbService!: Service; - - // Characteristic Values - On!: CharacteristicValue; - Hue!: CharacteristicValue; - Saturation!: CharacteristicValue; - Brightness!: CharacteristicValue; - ColorTemperature?: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_On: deviceStatus['power']; - OpenAPI_RGB: deviceStatus['color']; - OpenAPI_Brightness: deviceStatus['brightness']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_ColorTemperature?: deviceStatus['colorTemperature']; - - // BLE Status - BLE_Delay: serviceData['delay']; - BLE_On: serviceData['state']; - BLE_WifiRssi: serviceData['wifiRssi']; - - // BLE Others - BLE_IsConnected?: boolean; - - // Config - set_minStep?: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - adaptiveLightingShift?: number; - maxRetries!: number; - delayBetweenRetries!: number; + private LightBulb: { + Service: Service; + On: CharacteristicValue; + Hue: CharacteristicValue; + Saturation: CharacteristicValue; + Brightness: CharacteristicValue; + ColorTemperature?: CharacteristicValue; + }; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; - minKelvin!: number; - maxKelvin!: number; - - // Others - cacheKelvin!: number; + adaptiveLightingShift?: number; // Updates ceilingLightUpdateInProgress!: boolean; doCeilingLightUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + super(platform, accessory, device); // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); this.adaptiveLighting(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doCeilingLightUpdate = new Subject(); @@ -97,36 +51,34 @@ export class CeilingLight { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, this.model(device)) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize LightBulb property + this.LightBulb = { + Service: this.accessory.addService(this.hap.Service.Lightbulb), + On: accessory.context.On || false, + Hue: accessory.context.Hue || 0, + Saturation: accessory.context.Saturation || 0, + Brightness: accessory.context.Brightness || 0, + ColorTemperature: accessory.context.ColorTemperature || 140, + }; + + const LightBulbService = `${accessory.displayName} ${device.deviceType}`; + (this.LightBulb!.Service = accessory.getService(this.hap.Service.Lightbulb) + || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - // get the Lightbulb service if it exists, otherwise create a new Lightbulb service - // you can create multiple services for each accessory - const lightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService; if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { - this.accessory.removeService(this.lightBulbService); - this.lightBulbService = this.accessory.addService(this.hap.Service.Lightbulb); + this.accessory.removeService(this.LightBulb!.Service); + this.LightBulb!.Service = this.accessory.addService(this.hap.Service.Lightbulb); this.accessory.context.adaptiveLighting = false; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); } - this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.LightBulb!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the On characteristic - this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); // handle Brightness events using the Brightness characteristic - this.lightBulbService + this.LightBulb!.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ minStep: this.minStep(device), @@ -135,12 +87,12 @@ export class CeilingLight { validValueRanges: [0, 100], }) .onGet(() => { - return this.Brightness; + return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); // handle ColorTemperature events using the ColorTemperature characteristic - this.lightBulbService + this.LightBulb!.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ minValue: 140, @@ -148,12 +100,12 @@ export class CeilingLight { validValueRanges: [140, 500], }) .onGet(() => { - return this.ColorTemperature!; + return this.LightBulb.ColorTemperature!; }) .onSet(this.ColorTemperatureSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb!.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ minValue: 0, @@ -161,12 +113,12 @@ export class CeilingLight { validValueRanges: [0, 360], }) .onGet(() => { - return this.Hue; + return this.LightBulb.Hue; }) .onSet(this.HueSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb!.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ minValue: 0, @@ -174,13 +126,13 @@ export class CeilingLight { validValueRanges: [0, 100], }) .onGet(() => { - return this.Saturation; + return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.lightBulbService, { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb!.Service, { customTemperatureAdjustment: this.adaptiveLightingShift, }); this.accessory.configureController(this.AdaptiveLightingController); @@ -208,14 +160,14 @@ export class CeilingLight { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { powerState, brightness, colorTemperature } = context; - const { On, Brightness, ColorTemperature } = this; + const { On, Brightness, ColorTemperature } = this.LightBulb; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(powerState, brightness, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${ColorTemperature})`); - this.On = powerState === 'ON' ? true : false; - this.Brightness = brightness; - this.ColorTemperature = colorTemperature; + this.LightBulb.On = powerState === 'ON' ? true : false; + this.LightBulb.Brightness = brightness; + this.LightBulb.ColorTemperature = colorTemperature; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -247,69 +199,44 @@ export class CeilingLight { }); } - private model(device): CharacteristicValue { - let model: string; - if (device.deviceType === 'Ceiling Light') { - model = 'W2612230' || 'W2612240'; - } else if (device.deviceType === 'Ceiling Light Pro') { - model = 'W2612210' || 'W2612220'; - } else { - model = 'unknown'; - } - return model; - } - /** - * Parse the device status from the SwitchBot api + * Parse the device status from the SwitchBotBLE API */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } - /*if (this.BLE) { - await this.BLEparseStatus(); - } else*/ if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // State - switch (this.BLE_On) { + switch (serviceData.state) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } - async openAPIparseStatus() { + /** + * Parse the device status from the SwitchBot OpenAPI + */ + async openAPIparseStatus(deviceStatus: deviceStatus) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_On) { + switch (deviceStatus.body.power) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); // Brightness - this.Brightness = Number(this.OpenAPI_Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + this.LightBulb.Brightness = Number(deviceStatus.body.brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); // Color, Hue & Brightness - if (this.OpenAPI_RGB) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(this.OpenAPI_RGB)}`); - const [red, green, blue] = this.OpenAPI_RGB!.split(':'); + if (deviceStatus.body.color) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(deviceStatus.body.color)}`); + const [red, green, blue] = deviceStatus.body.color!.split(':'); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); @@ -320,28 +247,27 @@ export class CeilingLight { ); // Hue - this.Hue = hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); // Saturation - this.Saturation = saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } // ColorTemperature - if (!Number.isNaN(this.OpenAPI_ColorTemperature)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI ColorTemperature: ${this.OpenAPI_ColorTemperature}`); - const mired = Math.round(1000000 / this.OpenAPI_ColorTemperature!); + if (!Number.isNaN(deviceStatus.body.colorTemperature)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI ColorTemperature: ${deviceStatus.body.colorTemperature}`); + const mired = Math.round(1000000 / deviceStatus.body.colorTemperature!); - this.ColorTemperature = Number(mired); + this.LightBulb.ColorTemperature = Number(mired); - this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -376,98 +302,35 @@ export class CeilingLight { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); - if (this.device.bleMac === ad.address && ad.model === 's') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_On = ad.serviceData.state; - //this.delay = ad.serviceData.delay; - //this.timer = ad.serviceData.timer; - //this.syncUtcTime = ad.serviceData.syncUtcTime; - //this.wifiRssi = ad.serviceData.wifiRssi; - //this.overload = ad.serviceData.overload; - //this.currentPower = ad.serviceData.currentPower; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: '', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_On = ad.serviceData.state; - //this.delay = ad.serviceData.delay; - //this.timer = ad.serviceData.timer; - //this.syncUtcTime = ad.serviceData.syncUtcTime; - //this.wifiRssi = ad.serviceData.wifiRssi; - //this.overload = ad.serviceData.overload; - //this.currentPower = ad.serviceData.currentPower; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + - `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` + - `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } - async openAPIRefreshStatus() { + async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -475,12 +338,7 @@ export class CeilingLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.OpenAPI_RGB = deviceStatus.body.color; - this.OpenAPI_Brightness = deviceStatus.body.brightness; - this.OpenAPI_ColorTemperature = deviceStatus.body.colorTemperature; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -530,8 +388,9 @@ export class CeilingLight { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + if (this.LightBulb.On !== this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` BLEpushChanges On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -545,11 +404,11 @@ export class CeilingLight { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (this.LightBulb.On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -560,8 +419,8 @@ export class CeilingLight { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.On = false; + + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -573,16 +432,17 @@ export class CeilingLight { }); } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, + `${this.device.deviceType}: ${this.accessory.displayName}` + + ` No BLEpushChanges: On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } } async openAPIpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.On !== this.accessory.context.On) { + if (this.LightBulb.On !== this.accessory.context.On) { let command = ''; - if (this.On) { + if (this.LightBulb.On) { command = 'turnOn'; } else { command = 'turnOff'; @@ -623,30 +483,30 @@ export class CeilingLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } // Push Hue & Saturation Update - if (this.On) { + if (this.LightBulb.On) { await this.pushHueSaturationChanges(); } // Push ColorTemperature Update - if (this.On) { + if (this.LightBulb.On) { await this.pushColorTemperatureChanges(); } // Push Brightness Update - if (this.On) { + if (this.LightBulb.On) { await this.pushBrightnessChanges(); } } async pushHueSaturationChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushHueSaturationChanges`); - if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`); - const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`); + const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); const bodyChange = JSON.stringify({ command: 'setColor', @@ -683,17 +543,17 @@ export class CeilingLight { } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, + `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + + `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, ); } } async pushColorTemperatureChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushColorTemperatureChanges`); - if (this.ColorTemperature !== this.accessory.context.ColorTemperature) { - const kelvin = Math.round(1000000 / Number(this.ColorTemperature)); - this.cacheKelvin = kelvin; + if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { + const kelvin = Math.round(1000000 / Number(this.LightBulb.ColorTemperature)); + this.accessory.context.kelvin = kelvin; const bodyChange = JSON.stringify({ command: 'setColorTemperature', parameter: `${kelvin}`, @@ -728,17 +588,17 @@ export class CeilingLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + - `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, ); } } async pushBrightnessChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushBrightnessChanges`); - if (this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { const bodyChange = JSON.stringify({ command: 'setBrightness', - parameter: `${this.Brightness}`, + parameter: `${this.LightBulb.Brightness}`, commandType: 'command', }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); @@ -772,7 +632,7 @@ export class CeilingLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + - `Brightness: ${this.Brightness}, ` + + `Brightness: ${this.LightBulb.Brightness}, ` + `BrightnessCached: ${this.accessory.context.Brightness}`, ); } @@ -782,13 +642,13 @@ export class CeilingLight { * Handle requests to set the value of the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - if (this.On === this.accessory.context.On) { + if (this.LightBulb.On === this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); } - this.On = value; + this.LightBulb.On = value; this.doCeilingLightUpdate.next(); } @@ -796,15 +656,15 @@ export class CeilingLight { * Handle requests to set the value of the "Brightness" characteristic */ async BrightnessSet(value: CharacteristicValue): Promise { - if (this.Brightness === this.accessory.context.Brightness) { + if (this.LightBulb.Brightness === this.accessory.context.Brightness) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } - this.Brightness = value; + this.LightBulb.Brightness = value; this.doCeilingLightUpdate.next(); } @@ -812,30 +672,32 @@ export class CeilingLight { * Handle requests to set the value of the "ColorTemperature" characteristic */ async ColorTemperatureSet(value: CharacteristicValue): Promise { - if (this.ColorTemperature === this.accessory.context.ColorTemperature) { + if (this.LightBulb.ColorTemperature === this.accessory.context.ColorTemperature) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set ColorTemperature: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } + const minKelvin = 2000; + const maxKelvin = 9000; // Convert mired to kelvin to nearest 100 (SwitchBot seems to need this) const kelvin = Math.round(1000000 / Number(value) / 100) * 100; // Check and increase/decrease kelvin to range of device - const k = Math.min(Math.max(kelvin, this.minKelvin), this.maxKelvin); + const k = Math.min(Math.max(kelvin, minKelvin), maxKelvin); - if (!this.accessory.context.On || this.cacheKelvin === k) { + if (!this.accessory.context.On || this.accessory.context.kelvin === k) { return; } // Updating the hue/sat to the corresponding values mimics native adaptive lighting const hs = m2hs(value); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); - this.ColorTemperature = value; + this.LightBulb.ColorTemperature = value; this.doCeilingLightUpdate.next(); } @@ -843,17 +705,17 @@ export class CeilingLight { * Handle requests to set the value of the "Hue" characteristic */ async HueSet(value: CharacteristicValue): Promise { - if (this.Hue === this.accessory.context.Hue) { + if (this.LightBulb.Hue === this.accessory.context.Hue) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Hue: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Hue = value; + this.LightBulb.Hue = value; this.doCeilingLightUpdate.next(); } @@ -861,55 +723,56 @@ export class CeilingLight { * Handle requests to set the value of the "Saturation" characteristic */ async SaturationSet(value: CharacteristicValue): Promise { - if (this.Saturation === this.accessory.context.Saturation) { + if (this.LightBulb.Saturation === this.accessory.context.Saturation) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Saturation: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Saturation = value; + this.LightBulb.Saturation = value; this.doCeilingLightUpdate.next(); } async updateHomeKitCharacteristics(): Promise { - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.LightBulb.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } else { - this.accessory.context.On = this.On; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.LightBulb.On; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`); } - if (this.Brightness === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + if (this.LightBulb.Brightness === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); } else { - this.accessory.context.Brightness = this.Brightness; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); + this.accessory.context.Brightness = this.LightBulb.Brightness; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`); } - if (this.ColorTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + if (this.LightBulb.ColorTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } else { - this.accessory.context.ColorTemperature = this.ColorTemperature; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.ColorTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`); + this.accessory.context.ColorTemperature = this.LightBulb.ColorTemperature; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.LightBulb.ColorTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic ColorTemperature: ${this.LightBulb.ColorTemperature}`); } - if (this.Hue === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + if (this.LightBulb.Hue === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); } else { - this.accessory.context.Hue = this.Hue; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, this.Hue); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`); + this.accessory.context.Hue = this.LightBulb.Hue; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, this.LightBulb.Hue); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.LightBulb.Hue}`); } - if (this.Saturation === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + if (this.LightBulb.Saturation === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } else { - this.accessory.context.Saturation = this.Saturation; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, this.Saturation); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`); + this.accessory.context.Saturation = this.LightBulb.Saturation; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, this.LightBulb.Saturation); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.LightBulb.Saturation}`); } } @@ -923,35 +786,6 @@ export class CeilingLight { } } - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`); - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'u', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLEPushConnection() { if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); @@ -968,307 +802,27 @@ export class CeilingLight { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - minStep(device: device & devicesConfig): number { + let set_minStep: number; if (device.ceilinglight?.set_minStep) { - this.set_minStep = device.ceilinglight?.set_minStep; - } else { - this.set_minStep = 1; - } - return this.set_minStep; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } + set_minStep = device.ceilinglight?.set_minStep; } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_minStep = 1; } + return set_minStep; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } apiError(e: any): void { - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e); - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.Hue === undefined) { - this.Hue = 0; - } else { - this.Hue = this.accessory.context.Hue; - } - if (this.Brightness === undefined) { - this.Brightness = 0; - } else { - this.Brightness = this.accessory.context.Brightness; - } - if (this.Brightness === undefined) { - this.Saturation = 0; - } else { - this.Saturation = this.accessory.context.Saturation; - } - if (this.ColorTemperature === undefined) { - this.ColorTemperature = 140; - } else { - this.ColorTemperature = this.accessory.context.ColorTemperature; - } - this.minKelvin = 2000; - this.maxKelvin = 9000; - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.ceilinglight) { - config = device.ceilinglight; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, e); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Brightness, e); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, e); + this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e); } } diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index d1b83bde..ceedcc98 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -1,102 +1,49 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * blindtilt.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; +import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; -import { - Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, -} from 'homebridge'; +import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class ColorBulb { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class ColorBulb extends deviceBase { // Services - lightBulbService!: Service; - - // Characteristic Values - On!: CharacteristicValue; - Hue!: CharacteristicValue; - Saturation!: CharacteristicValue; - Brightness!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - ColorTemperature?: CharacteristicValue; - - // OpenAPI Status - OpenAPI_On: deviceStatus['power']; - OpenAPI_RGB: deviceStatus['color']; - OpenAPI_Brightness: deviceStatus['brightness']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_ColorTemperature?: deviceStatus['colorTemperature']; - - // BLE Status - BLE_ColorTemperature: serviceData['color_temperature']; - BLE_Power: serviceData['power']; - BLE_Red: serviceData['red']; - BLE_Blue: serviceData['blue']; - BLE_Green: serviceData['green']; - BLE_On: serviceData['state']; - BLE_Delay: serviceData['delay']; - BLE_WifiRssi: serviceData['wifiRssi']; - BLE_Brightness: serviceData['brightness']; - BLE_Saturation; - BLE_Hue; - - // BLE Others - BLE_IsConnected?: boolean; - - // Config - set_minStep?: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - adaptiveLightingShift?: number; - maxRetries!: number; - delayBetweenRetries!: number; + private LightBulb: { + Service: Service; + On: CharacteristicValue; + Hue: CharacteristicValue; + Saturation: CharacteristicValue; + Brightness: CharacteristicValue; + ColorTemperature?: CharacteristicValue; + }; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; - minKelvin!: number; - maxKelvin!: number; - - // Others - cacheKelvin!: number; + adaptiveLightingShift?: number; // Updates colorBulbUpdateInProgress!: boolean; doColorBulbUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + super(platform, accessory, device); // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); this.adaptiveLighting(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); + // this is subject we use to track when we need to POST changes to the SwitchBot API this.doColorBulbUpdate = new Subject(); @@ -105,37 +52,36 @@ export class ColorBulb { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W1401400') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize LightBulb property + this.LightBulb = { + Service: this.accessory.addService(this.hap.Service.Lightbulb), + On: accessory.context.On || false, + Hue: accessory.context.Hue || 0, + Saturation: accessory.context.Saturation || 0, + Brightness: accessory.context.Brightness || 0, + ColorTemperature: accessory.context.ColorTemperature || 140, + }; // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory const lightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb) + (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService; if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { - this.accessory.removeService(this.lightBulbService); - this.lightBulbService = this.accessory.addService(this.hap.Service.Lightbulb); + this.accessory.removeService(this.LightBulb.Service); + this.LightBulb.Service = this.accessory.addService(this.hap.Service.Lightbulb); this.accessory.context.adaptiveLighting = false; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); } - this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the On characteristic - this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); // handle Brightness events using the Brightness characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ minStep: this.minStep(device), @@ -144,12 +90,12 @@ export class ColorBulb { validValueRanges: [0, 100], }) .onGet(() => { - return this.Brightness; + return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); // handle ColorTemperature events using the ColorTemperature characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ minValue: 140, @@ -157,12 +103,12 @@ export class ColorBulb { validValueRanges: [140, 500], }) .onGet(() => { - return this.ColorTemperature!; + return this.LightBulb.ColorTemperature!; }) .onSet(this.ColorTemperatureSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ minValue: 0, @@ -170,12 +116,12 @@ export class ColorBulb { validValueRanges: [0, 360], }) .onGet(() => { - return this.Hue; + return this.LightBulb.Hue; }) .onSet(this.HueSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ minValue: 0, @@ -183,13 +129,13 @@ export class ColorBulb { validValueRanges: [0, 100], }) .onGet(() => { - return this.Saturation; + return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.lightBulbService, { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { customTemperatureAdjustment: this.adaptiveLightingShift, }); this.accessory.configureController(this.AdaptiveLightingController); @@ -217,13 +163,13 @@ export class ColorBulb { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { powerState, brightness, color, colorTemperature } = context; - const { On, Brightness, Hue, Saturation, ColorTemperature } = this; + const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(powerState, brightness, color, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); - this.On = powerState === 'ON' ? true : false; - this.Brightness = brightness; + this.LightBulb.On = powerState === 'ON' ? true : false; + this.LightBulb.Brightness = brightness; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); const [red, green, blue] = color!.split(':'); @@ -237,26 +183,26 @@ export class ColorBulb { ); // Hue - this.Hue = hue; - if (this.accessory.context.Hue !== this.Hue) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + if (this.accessory.context.Hue !== this.LightBulb.Hue) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); } // Saturation - this.Saturation = saturation; - if (this.accessory.context.Saturation !== this.Saturation) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + if (this.accessory.context.Saturation !== this.LightBulb.Saturation) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } - this.ColorTemperature = colorTemperature; - if (this.accessory.context.ColorTemperature !== this.ColorTemperature) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.LightBulb.ColorTemperature = colorTemperature; + if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -290,89 +236,75 @@ export class ColorBulb { } /** - * Parse the device status from the SwitchBot api + * Parse the device status from the SwitchBotBLE API */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // State - switch (this.BLE_On) { + switch (serviceData.power) { case true: - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); // Brightness - this.Brightness = Number(this.BLE_Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + this.LightBulb.Brightness = Number(serviceData.brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); // Color, Hue & Brightness - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${this.BLE_Red}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${this.BLE_Green}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${this.BLE_Blue}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${serviceData.red}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${serviceData.green}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${serviceData.blue}`); - const [hue, saturation] = rgb2hs(Number(this.BLE_Red), Number(this.BLE_Green), Number(this.BLE_Blue)); + const [hue, saturation] = rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName}` + - ` hs: ${JSON.stringify(rgb2hs(Number(this.BLE_Red), Number(this.BLE_Green), Number(this.BLE_Blue)))}`, + ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`, ); - this.BLE_Hue = hue; - this.BLE_Saturation = saturation; // Hue - this.Hue = this.BLE_Hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); // Saturation - this.Saturation = this.BLE_Saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); // ColorTemperature - if (this.BLE_ColorTemperature) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.BLE_ColorTemperature}`); - this.ColorTemperature = this.BLE_ColorTemperature!; + if (serviceData.color_temperature) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${serviceData.color_temperature}`); + this.LightBulb.ColorTemperature = serviceData.color_temperature!; - this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } } - async openAPIparseStatus() { + /** + * Parse the device status from the SwitchBot OpenAPI + */ + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_On) { + switch (deviceStatus.body.power) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); // Brightness - this.Brightness = Number(this.OpenAPI_Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + this.LightBulb.Brightness = Number(deviceStatus.body.brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); // Color, Hue & Brightness - if (this.OpenAPI_RGB) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(this.OpenAPI_RGB)}`); - const [red, green, blue] = this.OpenAPI_RGB!.split(':'); + if (deviceStatus.body.color) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(deviceStatus.body.color)}`); + const [red, green, blue] = deviceStatus.body.color!.split(':'); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); @@ -383,28 +315,27 @@ export class ColorBulb { ); // Hue - this.Hue = hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); // Saturation - this.Saturation = saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } // ColorTemperature - if (!Number.isNaN(this.OpenAPI_ColorTemperature)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.OpenAPI_ColorTemperature}`); - const mired = Math.round(1000000 / this.OpenAPI_ColorTemperature!); + if (!Number.isNaN(deviceStatus.body.colorTemperature)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${deviceStatus.body.colorTemperature}`); + const mired = Math.round(1000000 / deviceStatus.body.colorTemperature!); - this.ColorTemperature = Number(mired); + this.LightBulb.ColorTemperature = Number(mired); - this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -439,101 +370,35 @@ export class ColorBulb { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'u', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'u') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_ColorTemperature = ad.serviceData.color_temperature; - this.BLE_Power = ad.serviceData.power; - this.BLE_On = ad.serviceData.state; - this.BLE_Red = ad.serviceData.red; - this.BLE_Green = ad.serviceData.green; - this.BLE_Blue = ad.serviceData.blue; - this.BLE_Brightness = ad.serviceData.brightness; - this.BLE_Delay = ad.serviceData.delay; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'u', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_Power = ad.serviceData.power; - this.BLE_On = ad.serviceData.state; - this.BLE_Red = ad.serviceData.red; - this.BLE_Green = ad.serviceData.green; - this.BLE_Blue = ad.serviceData.blue; - this.BLE_ColorTemperature = ad.serviceData.color_temperature; - this.BLE_Brightness = ad.serviceData.brightness; - this.BLE_Delay = ad.serviceData.delay; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + - `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` + - `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } - async openAPIRefreshStatus() { + async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -541,12 +406,7 @@ export class ColorBulb { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.OpenAPI_RGB = deviceStatus.body.color; - this.OpenAPI_Brightness = deviceStatus.body.brightness; - this.OpenAPI_ColorTemperature = deviceStatus.body.colorTemperature; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -596,8 +456,9 @@ export class ColorBulb { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + if (this.LightBulb.On !== this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` + + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -611,11 +472,11 @@ export class ColorBulb { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (this.LightBulb.On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -626,8 +487,8 @@ export class ColorBulb { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.On = false; + + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -638,27 +499,28 @@ export class ColorBulb { await this.BLEPushConnection(); }); // Push Brightness Update - if (this.On) { + if (this.LightBulb.On) { await this.BLEpushBrightnessChanges(); } // Push ColorTemperature Update - if (this.On) { + if (this.LightBulb.On) { await this.BLEpushColorTemperatureChanges(); } // Push Hue & Saturation Update - if (this.On) { + if (this.LightBulb.On) { await this.BLEpushRGBChanges(); } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, + `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } } async BLEpushBrightnessChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushBrightnessChanges`); - if (this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -672,12 +534,12 @@ export class ColorBulb { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.Brightness}`); - return await device_list[0].setBrightness(this.Brightness); + this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.LightBulb.Brightness}`); + return await device_list[0].setBrightness(this.LightBulb.Brightness); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.On = false; + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -690,7 +552,7 @@ export class ColorBulb { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + - `Brightness: ${this.Brightness}, ` + + `Brightness: ${this.LightBulb.Brightness}, ` + `BrightnessCached: ${this.accessory.context.Brightness}`, ); } @@ -698,7 +560,7 @@ export class ColorBulb { async BLEpushColorTemperatureChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushColorTemperatureChanges`); - if (this.ColorTemperature !== this.accessory.context.ColorTemperature) { + if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -712,12 +574,12 @@ export class ColorBulb { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target ColorTemperature: ${this.ColorTemperature}`); - return await device_list[0].setColorTemperature(this.ColorTemperature); + this.infoLog(`${this.accessory.displayName} Target ColorTemperature: ${this.LightBulb.ColorTemperature}`); + return await device_list[0].setColorTemperature(this.LightBulb.ColorTemperature); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.On = false; + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -730,18 +592,18 @@ export class ColorBulb { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushColorTemperatureChanges.` + - `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, ); } } async BLEpushRGBChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushRGBChanges`); - if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`); + if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`); - const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); const switchbot = await this.platform.connectBLE(); @@ -757,12 +619,12 @@ export class ColorBulb { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.Brightness, red, green, blue)}`); - return await device_list[0].setRGB(this.Brightness, red, green, blue); + this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.LightBulb.Brightness, red, green, blue)}`); + return await device_list[0].setRGB(this.LightBulb.Brightness, red, green, blue); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.On = false; + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -774,17 +636,17 @@ export class ColorBulb { }); } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, + `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, ` + + `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, ); } } async openAPIpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.On !== this.accessory.context.On) { + if (this.LightBulb.On !== this.accessory.context.On) { let command = ''; - if (this.On) { + if (this.LightBulb.On) { command = 'turnOn'; } else { command = 'turnOff'; @@ -825,30 +687,30 @@ export class ColorBulb { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } // Push Hue & Saturation Update - if (this.On) { + if (this.LightBulb.On) { await this.pushHueSaturationChanges(); } // Push ColorTemperature Update - if (this.On) { + if (this.LightBulb.On) { await this.pushColorTemperatureChanges(); } // Push Brightness Update - if (this.On) { + if (this.LightBulb.On) { await this.pushBrightnessChanges(); } } async pushHueSaturationChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushHueSaturationChanges`); - if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`); - const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`); + const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); const bodyChange = JSON.stringify({ command: 'setColor', @@ -871,7 +733,7 @@ export class ColorBulb { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -885,17 +747,17 @@ export class ColorBulb { } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, + `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + + `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, ); } } async pushColorTemperatureChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushColorTemperatureChanges`); - if (this.ColorTemperature !== this.accessory.context.ColorTemperature) { - const kelvin = Math.round(1000000 / Number(this.ColorTemperature)); - this.cacheKelvin = kelvin; + if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { + const kelvin = Math.round(1000000 / Number(this.LightBulb.ColorTemperature)); + this.accessory.context.kelvin = kelvin; const bodyChange = JSON.stringify({ command: 'setColorTemperature', parameter: `${kelvin}`, @@ -917,32 +779,28 @@ export class ColorBulb { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + - `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); } } async pushBrightnessChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushBrightnessChanges`); - if (this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { const bodyChange = JSON.stringify({ command: 'setBrightness', - parameter: `${this.Brightness}`, + parameter: `${this.LightBulb.Brightness}`, commandType: 'command', }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); @@ -961,7 +819,7 @@ export class ColorBulb { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -976,7 +834,7 @@ export class ColorBulb { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + - `Brightness: ${this.Brightness}, ` + + `Brightness: ${this.LightBulb.Brightness}, ` + `BrightnessCached: ${this.accessory.context.Brightness}`, ); } @@ -986,13 +844,13 @@ export class ColorBulb { * Handle requests to set the value of the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - if (this.On === this.accessory.context.On) { + if (this.LightBulb.On === this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); } - this.On = value; + this.LightBulb.On = value; this.doColorBulbUpdate.next(); } @@ -1000,15 +858,15 @@ export class ColorBulb { * Handle requests to set the value of the "Brightness" characteristic */ async BrightnessSet(value: CharacteristicValue): Promise { - if (this.Brightness === this.accessory.context.Brightness) { + if (this.LightBulb.Brightness === this.accessory.context.Brightness) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } - this.Brightness = value; + this.LightBulb.Brightness = value; this.doColorBulbUpdate.next(); } @@ -1016,30 +874,32 @@ export class ColorBulb { * Handle requests to set the value of the "ColorTemperature" characteristic */ async ColorTemperatureSet(value: CharacteristicValue): Promise { - if (this.ColorTemperature === this.accessory.context.ColorTemperature) { + if (this.LightBulb.ColorTemperature === this.accessory.context.ColorTemperature) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set ColorTemperature: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } + const minKelvin = 2000; + const maxKelvin = 9000; // Convert mired to kelvin to nearest 100 (SwitchBot seems to need this) const kelvin = Math.round(1000000 / Number(value) / 100) * 100; // Check and increase/decrease kelvin to range of device - const k = Math.min(Math.max(kelvin, this.minKelvin), this.maxKelvin); + const k = Math.min(Math.max(kelvin, minKelvin), maxKelvin); - if (!this.accessory.context.On || this.cacheKelvin === k) { + if (!this.accessory.context.On || this.accessory.context.kelvin === k) { return; } // Updating the hue/sat to the corresponding values mimics native adaptive lighting const hs = m2hs(value); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); - this.ColorTemperature = value; + this.LightBulb.ColorTemperature = value; this.doColorBulbUpdate.next(); } @@ -1047,17 +907,17 @@ export class ColorBulb { * Handle requests to set the value of the "Hue" characteristic */ async HueSet(value: CharacteristicValue): Promise { - if (this.Hue === this.accessory.context.Hue) { + if (this.LightBulb.Hue === this.accessory.context.Hue) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Hue: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Hue = value; + this.LightBulb.Hue = value; this.doColorBulbUpdate.next(); } @@ -1065,60 +925,61 @@ export class ColorBulb { * Handle requests to set the value of the "Saturation" characteristic */ async SaturationSet(value: CharacteristicValue): Promise { - if (this.Saturation === this.accessory.context.Saturation) { + if (this.LightBulb.Saturation === this.accessory.context.Saturation) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Saturation: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Saturation = value; + this.LightBulb.Saturation = value; this.doColorBulbUpdate.next(); } async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.LightBulb.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } else { - this.accessory.context.On = this.On; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.LightBulb.On; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`); } // Brightness - if (this.Brightness === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + if (this.LightBulb.Brightness === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); } else { - this.accessory.context.Brightness = this.Brightness; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); + this.accessory.context.Brightness = this.LightBulb.Brightness; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`); } // ColorTemperature - if (this.ColorTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + if (this.LightBulb.ColorTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } else { - this.accessory.context.ColorTemperature = this.ColorTemperature; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.ColorTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`); + this.accessory.context.ColorTemperature = this.LightBulb.ColorTemperature; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.LightBulb.ColorTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ColorTemperature: ${this.LightBulb.ColorTemperature}`); } // Hue - if (this.Hue === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + if (this.LightBulb.Hue === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); } else { - this.accessory.context.Hue = this.Hue; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, this.Hue); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`); + this.accessory.context.Hue = this.LightBulb.Hue; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, this.LightBulb.Hue); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.LightBulb.Hue}`); } // Saturation - if (this.Saturation === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + if (this.LightBulb.Saturation === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } else { - this.accessory.context.Saturation = this.Saturation; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, this.Saturation); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`); + this.accessory.context.Saturation = this.LightBulb.Saturation; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, this.LightBulb.Saturation); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.LightBulb.Saturation}`); } } @@ -1132,35 +993,6 @@ export class ColorBulb { } } - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`); - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'u', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLEPushConnection() { if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); @@ -1177,307 +1009,27 @@ export class ColorBulb { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - minStep(device: device & devicesConfig): number { + let set_minStep: number; if (device.colorbulb?.set_minStep) { - this.set_minStep = device.colorbulb?.set_minStep; - } else { - this.set_minStep = 1; - } - return this.set_minStep; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } + set_minStep = device.colorbulb?.set_minStep; } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_minStep = 1; } + return set_minStep; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } apiError(e: any): void { - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e); - } - - async deviceContext(): Promise { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.Hue === undefined) { - this.Hue = 0; - } else { - this.Hue = this.accessory.context.Hue; - } - if (this.Brightness === undefined) { - this.Brightness = 0; - } else { - this.Brightness = this.accessory.context.Brightness; - } - if (this.Saturation === undefined) { - this.Saturation = 0; - } else { - this.Saturation = this.accessory.context.Saturation; - } - if (this.ColorTemperature === undefined) { - this.ColorTemperature = 140; - } else { - this.ColorTemperature = this.accessory.context.ColorTemperature; - } - this.minKelvin = 2000; - this.maxKelvin = 9000; - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.colorbulb) { - config = device.colorbulb; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e); } } diff --git a/src/device/contact.ts b/src/device/contact.ts index bf90d719..0f9cfb31 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -1,166 +1,136 @@ -import { sleep } from '../utils.js'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * contact.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Contact { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Contact extends deviceBase { // Services - motionService?: Service; - batteryService: Service; - lightSensorService?: Service; - contactSensorservice: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - MotionDetected!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - ContactSensorState!: CharacteristicValue; - CurrentAmbientLightLevel!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_MotionDetected: deviceStatus['moveDetected']; - OpenAPI_ContactSensorState: deviceStatus['openState']; - OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness']; - - // BLE Status - BLE_BatteryLevel!: serviceData['battery']; - BLE_MotionDetected!: serviceData['movement']; - BLE_ContactSensorState!: serviceData['doorState']; - BLE_CurrentAmbientLightLevel: serviceData['lightLevel']; - - // BLE Others - scanning!: boolean; - BLE_IsConnected?: boolean; - - // Config - set_minLux!: number; - set_maxLux!: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private ContactSensor!: { + Service: Service; + ContactSensorState: CharacteristicValue; + }; + + private MotionSensor?: { + Service: Service; + MotionDetected: CharacteristicValue; + }; + + private LightSensor?: { + Service: Service; + CurrentAmbientLightLevel: CharacteristicValue; + }; + + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; // Updates - contactUbpdateInProgress!: boolean; + contactUpdateInProgress!: boolean; doContactUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doContactUpdate = new Subject(); - this.contactUbpdateInProgress = false; + this.contactUpdateInProgress = false; // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W1201500') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Contact Sensor property + this.ContactSensor = { + Service: this.accessory.addService(this.hap.Service.ContactSensor), + ContactSensorState: this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, + }; + + // Initialize Motion Sensor property + if (!this.device.contact?.hide_motionsensor) { + this.MotionSensor = { + Service: this.accessory.addService(this.hap.Service.MotionSensor), + MotionDetected: false, + }; + } + + // Initialize Light Sensor property + if (!this.device.contact?.hide_lightsensor) { + this.LightSensor = { + Service: this.accessory.addService(this.hap.Service.LightSensor), + CurrentAmbientLightLevel: 0, + }; + } // get the Contact service if it exists, otherwise create a new Contact service // you can create multiple services for each accessory - const contactSensorservice = `${accessory.displayName} Contact Sensor`; - (this.contactSensorservice = accessory.getService(this.hap.Service.ContactSensor) - || accessory.addService(this.hap.Service.ContactSensor)), contactSensorservice; + const ContactSensorService = `${accessory.displayName} Contact Sensor`; + (this.ContactSensor.Service = accessory.getService(this.hap.Service.ContactSensor) + || accessory.addService(this.hap.Service.ContactSensor)), ContactSensorService; - this.contactSensorservice.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.contactSensorservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.contactSensorservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.ContactSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // Motion Sensor Service if (device.contact?.hide_motionsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); - this.motionService = this.accessory.getService(this.hap.Service.MotionSensor); - accessory.removeService(this.motionService!); - } else if (!this.motionService) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Motion Sensor Service`); - const motionService = `${accessory.displayName} Motion Sensor`; - (this.motionService = this.accessory.getService(this.hap.Service.MotionSensor) - || this.accessory.addService(this.hap.Service.MotionSensor)), motionService; - - this.motionService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Motion Sensor`); - this.motionService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Motion Sensor`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); + this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; + accessory.removeService(this.MotionSensor!.Service); + } else if (!this.MotionSensor!.Service && !device.contact?.hide_motionsensor) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Add Motion Sensor Service`); + const MotionService = `${accessory.displayName} Motion Sensor`; + (this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) + || accessory.addService(this.hap.Service.MotionSensor)), MotionService; + + this.MotionSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, MotionService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Motion Sensor Service Not Added`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} Motion Sensor Service Not Added`); } // Light Sensor Service if (device.contact?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor); - accessory.removeService(this.lightSensorService!); - } else if (!this.lightSensorService) { + this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor!.Service); + } else if (!this.LightSensor!.Service && !device.contact?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const lightSensorService = `${accessory.displayName} Light Sensor`; - (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService; + const LightSensorService = `${accessory.displayName} Light Sensor`; + (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) + || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`); - this.lightSensorService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`); + this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); // Start an update interval interval(this.deviceRefreshRate * 1000) - .pipe(skipWhile(() => this.contactUbpdateInProgress)) + .pipe(skipWhile(() => this.contactUpdateInProgress)) .subscribe(async () => { await this.refreshStatus(); }); @@ -172,16 +142,22 @@ export class Contact { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { detectionState, brightness, openState } = context; - const { MotionDetected, CurrentAmbientLightLevel, ContactSensorState } = this; + const { ContactSensorState } = this.ContactSensor; + const { CurrentAmbientLightLevel } = this.LightSensor || {}; + const { MotionDetected } = this.MotionSensor || {}; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(detectionState, brightness, openState) = ' + `Webhook:(${detectionState}, ${brightness}, ${openState}), ` + `current:(${MotionDetected}, ${CurrentAmbientLightLevel}, ${ContactSensorState})`); - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - this.MotionDetected = detectionState === 'DETECTED' ? true : false; - this.CurrentAmbientLightLevel = brightness === 'bright' ? this.set_maxLux : this.set_minLux; - this.ContactSensorState = openState === 'open' ? 1 : 0; + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + this.ContactSensor.ContactSensorState = openState === 'open' ? 1 : 0; + if (!this.device.contact?.hide_motionsensor) { + this.MotionSensor!.MotionDetected = detectionState === 'DETECTED' ? true : false; + } + if (!this.device.contact?.hide_lightsensor) { + this.LightSensor!.CurrentAmbientLightLevel = brightness === 'bright' ? set_maxLux : set_minLux; + } this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -191,136 +167,119 @@ export class Contact { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // Door State - switch (this.BLE_ContactSensorState) { + switch (serviceData.doorState) { case 'open': case 1: - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; + this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; break; case 'close': case 0: - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; + this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; break; default: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} timeout no closed, doorstate: ${this.BLE_ContactSensorState}`); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} timeout no closed, doorstate: ${serviceData.doorState}`); } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`); if ( - this.ContactSensorState !== this.accessory.context.ContactSensorState && + this.ContactSensor.ContactSensorState !== this.accessory.context.ContactSensorState && this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED ) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Opened`); } // Movement if (!this.device.contact?.hide_motionsensor) { - this.MotionDetected = Boolean(this.BLE_MotionDetected); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); - if (this.MotionDetected !== this.accessory.context.MotionDetected && this.MotionDetected) { + this.MotionSensor!.MotionDetected = Boolean(serviceData.movement); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`); + if (this.MotionSensor!.MotionDetected !== this.accessory.context.MotionDetected && this.MotionSensor!.MotionDetected) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Detected Motion`); } } // Light Level if (!this.device.contact?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - switch (this.BLE_CurrentAmbientLightLevel) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + switch (serviceData.lightLevel) { case true: - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); - if (this.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } } // Battery - if (this.BLE_BatteryLevel === undefined) { - this.BLE_BatteryLevel === 100; + if (serviceData.battery === undefined) { + serviceData.battery === 100; } - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, ` + `StatusLowBattery: ${this.StatusLowBattery}`, + `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, ` + + `StatusLowBattery: ${this.Battery.StatusLowBattery}`, ); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // Contact State - if (this.OpenAPI_ContactSensorState === 'open') { - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); - } else if (this.OpenAPI_ContactSensorState === 'close') { - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + if (deviceStatus.body.openState === 'open') { + this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`); + } else if (deviceStatus.body.openState === 'close') { + this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openState: ${this.OpenAPI_ContactSensorState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openState: ${deviceStatus.body.openState}`); } // Motion State if (!this.device.contact?.hide_motionsensor) { - this.MotionDetected = this.OpenAPI_MotionDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + this.MotionSensor!.MotionDetected = deviceStatus.body.moveDetected!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`); } // Light Level if (!this.device.contact?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - switch (this.OpenAPI_CurrentAmbientLightLevel) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + switch (deviceStatus.body.brightness) { case 'dim': - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; case 'bright': default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // Battery - if (this.OpenAPI_BatteryLevel === undefined) { - this.OpenAPI_BatteryLevel === 100; + if (deviceStatus.body.battery === undefined) { + deviceStatus.body.battery === 100; } - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, ` + `StatusLowBattery: ${this.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, ` + + `StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -346,103 +305,44 @@ export class Contact { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address - this.device.bleMac = - this.device.customBLEaddress || - this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); + this.device.bleMac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { - await switchbot.startScan({ - model: 'd', - id: this.device.bleMac, - }); + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'd') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_MotionDetected = ad.serviceData.movement; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - await switchbot - .startScan({ - model: 'd', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - this.scanning = true; - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_MotionDetected = ad.serviceData.movement; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_ContactSensorState = ad.serviceData.doorState; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, doorState: ` + - `${ad.serviceData.doorState}, battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - this.scanning = false; - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - if (this.scanning) { - await this.stopScanning(switchbot); - } - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -450,16 +350,14 @@ export class Contact { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_ContactSensorState = deviceStatus.body.openState; - this.OpenAPI_MotionDetected = deviceStatus.body.moveDetected; - this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); + if (deviceStatus.statusCode === 161 || deviceStatus.statusCode === 171) { + this.offlineOff(); + } } } catch (e: any) { this.apiError(e); @@ -474,75 +372,51 @@ export class Contact { * Updates the status for each of the HomeKit Characteristics */ async updateHomeKitCharacteristics(): Promise { - if (this.ContactSensorState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + if (this.ContactSensor.ContactSensorState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`); } else { - this.accessory.context.ContactSensorState = this.ContactSensorState; - this.contactSensorservice.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensorState); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`); + this.accessory.context.ContactSensorState = this.ContactSensor.ContactSensorState; + this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensor.ContactSensorState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ContactSensorState: ${this.ContactSensor.ContactSensorState}`); } if (!this.device.contact?.hide_motionsensor) { - if (this.MotionDetected === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + if (this.MotionSensor!.MotionDetected === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`); } else { - this.accessory.context.MotionDetected = this.MotionDetected; - this.motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionDetected); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); + this.accessory.context.MotionDetected = this.MotionSensor!.MotionDetected; + this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionSensor!.MotionDetected); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` MotionDetected: ${this.MotionSensor!.MotionDetected}`); } } if (!this.device.contact?.hide_lightsensor) { - if (this.CurrentAmbientLightLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor?.CurrentAmbientLightLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { - this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel; - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.accessory.context.CurrentAmbientLightLevel = this.LightSensor.CurrentAmbientLightLevel; + this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`, ); } } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'd', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } @@ -555,277 +429,48 @@ export class Contact { } } - minLux(): number { + async minLux(): Promise { + let set_minLux: number; if (this.device.contact?.set_minLux) { - this.set_minLux = this.device.contact!.set_minLux!; + set_minLux = this.device.contact!.set_minLux!; } else { - this.set_minLux = 1; + set_minLux = 1; } - return this.set_minLux; + return set_minLux; } - maxLux(): number { + async maxLux(): Promise { + let set_maxLux: number; if (this.device.contact?.set_maxLux) { - this.set_maxLux = this.device.contact!.set_maxLux!; + set_maxLux = this.device.contact!.set_maxLux!; } else { - this.set_maxLux = 6001; - } - return this.set_maxLux; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_maxLux = 6001; } + return set_maxLux; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, + this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED); + if (!this.device.contact?.hide_motionsensor) { + this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); + } + if (!this.device.contact?.hide_lightsensor) { + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, 100); + } } } async apiError(e: any): Promise { - this.contactSensorservice.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e); + this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e); if (!this.device.contact?.hide_motionsensor) { - this.motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, e); + this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, e); } if (!this.device.contact?.hide_lightsensor) { - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); - } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - - async deviceContext() { - if (this.MotionDetected === undefined) { - this.MotionDetected = false; - } else { - this.MotionDetected = this.accessory.context.MotionDetected; - } - if (this.ContactSensorState === undefined) { - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; - } else { - this.ContactSensorState = this.accessory.context.ContactSensorState; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.contact) { - config = device.contact; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 60983603..7efd9b81 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -1,108 +1,53 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * curtain.ts: @switchbot/homebridge-switchbot. + */ +import { hostname } from 'os'; import { request } from 'undici'; -import { sleep } from '../utils.js'; -import { MqttClient } from 'mqtt'; import { interval, Subject } from 'rxjs'; -import asyncmqtt from 'async-mqtt'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; -import { hostname } from 'os'; +import { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; -export class Curtain { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Curtain extends deviceBase { // Services - batteryService: Service; - lightSensorService?: Service; - windowCoveringService!: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - HoldPosition!: CharacteristicValue; - PositionState!: CharacteristicValue; - TargetPosition!: CharacteristicValue; - CurrentPosition!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - CurrentAmbientLightLevel?: CharacteristicValue; - - // OpenAPI Status - OpenAPI_InMotion: deviceStatus['moving']; - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentPosition: deviceStatus['slidePosition']; - OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness']; - - // Others - Mode!: string; - setPositionMode?: string; - - // BLE Status - BLE_InMotion: serviceData['inMotion']; - BLE_BatteryLevel: serviceData['battery']; - BLE_Calibration: serviceData['calibration']; - BLE_CurrentPosition: serviceData['position']; - BLE_CurrentAmbientLightLevel: serviceData['lightLevel']; - - // BLE Others - BLE_IsConnected?: boolean; - spaceBetweenLevels!: number; + private WindowCovering!: { + Service: Service; + PositionState: CharacteristicValue; + TargetPosition: CharacteristicValue; + CurrentPosition: CharacteristicValue; + HoldPosition: CharacteristicValue; + }; + + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private LightSensor?: { + Service: Service; + CurrentAmbientLightLevel?: CharacteristicValue; + }; // Target setNewTarget!: boolean; setNewTargetTimer!: NodeJS.Timeout; - //MQTT stuff - mqttClient: MqttClient | null = null; - - // Config - updateRate!: number; - set_minLux!: number; - set_maxLux!: number; - set_minStep!: number; - setOpenMode!: string; - setCloseMode!: string; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; - // Updates curtainUpdateInProgress!: boolean; doCurtainUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - - // EVE history service handler - historyService: any = null; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.refreshRate(device); - this.scan(device); - this.setupMqtt(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); + // default placeholder + this.history(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doCurtainUpdate = new Subject(); this.curtainUpdateInProgress = false; @@ -110,33 +55,45 @@ export class Curtain { // Retrieve initial values and updateHomekit this.refreshStatus(); - - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W0701600') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize LightBulb property + this.WindowCovering = { + Service: this.accessory.addService(this.hap.Service.WindowCovering), + PositionState: this.accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: this.accessory.context.TargetPosition || 100, + CurrentPosition: this.accessory.context.CurrentPosition || 100, + HoldPosition: this.accessory.context.HoldPosition || false, + }; + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: this.accessory.context.BatteryLevel || 100, + StatusLowBattery: this.accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; + + // Initialize LightSensor property + if (!this.device.curtain?.hide_lightsensor) { + this.LightSensor = { + Service: this.accessory.addService(this.hap.Service.LightSensor), + CurrentAmbientLightLevel: this.accessory.context.CurrentAmbientLightLevel || 0, + }; + } // get the WindowCovering service if it exists, otherwise create a new WindowCovering service // you can create multiple services for each accessory - const windowCoveringService = `${accessory.displayName} ${device.deviceType}`; - (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService; + const WindowCoveringService = `${accessory.displayName} ${device.deviceType}`; + (this.WindowCovering.Service = accessory.getService(this.hap.Service.WindowCovering) + || accessory.addService(this.hap.Service.WindowCovering)), WindowCoveringService; - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/WindowCovering // create handlers for required characteristics - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState); + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); - this.windowCoveringService + this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ minStep: this.minStep(device), @@ -145,10 +102,10 @@ export class Curtain { validValueRanges: [0, 100], }) .onGet(() => { - return this.CurrentPosition; + return this.WindowCovering.CurrentPosition; }); - this.windowCoveringService + this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ minStep: this.minStep(device), @@ -158,38 +115,32 @@ export class Curtain { }) .onSet(this.TargetPositionSet.bind(this)); - this.windowCoveringService + this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.HoldPosition) .onSet(this.HoldPositionSet.bind(this)); // Light Sensor Service if (device.curtain?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor); - accessory.removeService(this.lightSensorService!); - } else if (!this.lightSensorService) { + this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor!.Service); + } else if (!this.LightSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const lightSensorService = `${accessory.displayName} Light Sensor`; - (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService; + const LightSensorService = `${accessory.displayName} Light Sensor`; + (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) + || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`); - if (!this.lightSensorService?.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lightSensorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`); - } + this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); } // Battery Service const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) || accessory.addService(this.hap.Service.Battery)), batteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); // Update Homekit this.updateHomeKitCharacteristics(); @@ -208,13 +159,12 @@ export class Curtain { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { slidePosition, battery } = context; - const { CurrentPosition, BatteryLevel } = this; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(slidePosition, battery) = ' + `Webhook:(${slidePosition}, ${battery}), ` + - `current:(${CurrentPosition}, ${BatteryLevel})`); - this.CurrentPosition = slidePosition; - this.BatteryLevel = battery; + `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`); + this.WindowCovering.CurrentPosition = slidePosition; + this.Battery.BatteryLevel = battery; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -224,13 +174,14 @@ export class Curtain { } // update slide progress - interval(this.updateRate * 1000) + interval(this.deviceUpdateRate * 1000) //.pipe(skipWhile(() => this.curtainUpdateInProgress)) .subscribe(async () => { - if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) { + if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) { return; } - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,` + + ` PositionState: ${this.WindowCovering.PositionState}`); await this.refreshStatus(); }); @@ -260,6 +211,16 @@ export class Curtain { this.setupHistoryService(device); } + private history(device: device & devicesConfig) { + if (device.history === true) { + // initialize when this accessory is newly created. + this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0; + } else { + // removes cached values if history is turned off + delete this.accessory.context.lastActivation; + } + } + /* * Setup EVE history features for curtain devices. */ @@ -307,7 +268,7 @@ export class Curtain { } async updateHistory(): Promise { - const motion = Number(this.CurrentPosition) > 0 ? 1 : 0; + const motion = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0; this.historyService.addEntry({ time: Math.round(new Date().valueOf() / 1000), motion: motion, @@ -320,198 +281,189 @@ export class Curtain { ); } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // CurrentPosition - this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition); + this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position); await this.setMinMax(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`); if (this.setNewTarget) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`); await this.setMinMax(); - if (Number(this.TargetPosition) > this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`); - } else if (Number(this.TargetPosition) < this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`); + if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); + } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` + + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`); - this.TargetPosition = this.CurrentPosition; - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + - ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`, + `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, ); if (!this.device.curtain?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - this.spaceBetweenLevels = 9; + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + const spaceBetweenLevels = 9; // Brightness - switch (this.BLE_CurrentAmbientLightLevel) { + switch (serviceData.lightLevel) { case 1: - this.CurrentAmbientLightLevel = this.set_minLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 2: - this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels; + this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`, ); break; case 3: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 4: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 5: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 6: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 7: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 8: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 9: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`); + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); break; case 10: default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; this.debugLog(); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`, + `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, ); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // CurrentPosition - this.CurrentPosition = 100 - Number(this.OpenAPI_CurrentPosition); + this.WindowCovering.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition); await this.setMinMax(); - this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`); + this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); if (this.setNewTarget) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`); } - if (this.setNewTarget && this.OpenAPI_InMotion) { + if (this.setNewTarget && deviceStatus.body.moving) { await this.setMinMax(); - if (Number(this.TargetPosition) > this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `); - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`); - } else if (Number(this.TargetPosition) < this.CurrentPosition) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `); - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`); + if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); + } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` + + ` PositionState: ${this.WindowCovering.PositionState}`); } else { - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`); - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); - this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` + + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`); - this.TargetPosition = this.CurrentPosition; - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + - ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`, + `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, ); // Brightness if (!this.device.curtain?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - switch (this.OpenAPI_CurrentAmbientLightLevel) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + switch (deviceStatus.body.brightness) { case 'dim': - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; case 'bright': default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } async refreshStatus(): Promise { @@ -541,106 +493,37 @@ export class Curtain { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets - let model: string; - switch (this.device.configDeviceType) { - case 'Curtain3': - model = '{'; - break; - default: - model = 'c'; - } (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: model, - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'c') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_InMotion = ad.serviceData.inMotion; - this.BLE_CurrentPosition = ad.serviceData.position; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - - - /*if (switchbot !== false) { - switchbot - .startScan({ - model: model, - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_InMotion = ad.serviceData.inMotion; - this.BLE_CurrentPosition = ad.serviceData.position; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` + - `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` + - `inMotion: ${ad.serviceData.inMotion}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -648,12 +531,7 @@ export class Curtain { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentPosition = deviceStatus.body.slidePosition; - this.OpenAPI_InMotion = deviceStatus.body.moving; - this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -692,7 +570,7 @@ export class Curtain { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.TargetPosition !== this.CurrentPosition) { + if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) { const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -700,29 +578,29 @@ export class Curtain { .join(':') .toLowerCase(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - this.SilentPerformance(); - const adjustedMode = this.setPositionMode === '1' ? 0x01 : 0xff; - this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`); + const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance(); + const adjustedMode = setPositionMode === 1 ? 0x01 : 0xff; + this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`); if (switchbot !== false) { try { const device_list = await switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }); - this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`); + this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`); - await this.retry({ - max: this.maxRetry(), + await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode); + await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode); }, }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `TargetPosition: ${this.TargetPosition} sent over BLE, sent successfully`); + + `TargetPosition: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`); } catch (e) { this.apiError(e); this.errorLog( `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`, + ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`, ); await this.BLEPushConnection(); throw new Error('Connection error'); @@ -734,54 +612,23 @@ export class Curtain { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`, + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, ); } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - async openAPIpushChanges(): Promise { let command: string; let parameter: string; let commandType: string; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.TargetPosition !== this.CurrentPosition || this.device.disableCaching) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition}`); - const adjustedTargetPosition = 100 - Number(this.TargetPosition); - if (Number(this.TargetPosition) > 50) { - this.setPositionMode = this.device.curtain?.setOpenMode; - } else { - this.setPositionMode = this.device.curtain?.setCloseMode; - } - if (this.setPositionMode === '1') { - this.Mode = 'Silent Mode'; - } else if (this.setPositionMode === '0') { - this.Mode = 'Performance Mode'; - } else { - this.Mode = 'Default Mode'; - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`); - const adjustedMode = this.setPositionMode || 'ff'; - if (this.HoldPosition) { + if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition || this.device.disableCaching) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.WindowCovering.TargetPosition}`); + const adjustedTargetPosition = 100 - Number(this.WindowCovering.TargetPosition); + const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`); + const adjustedMode = setPositionMode || 'ff'; + if (this.WindowCovering.HoldPosition) { command = 'pause'; parameter = 'default'; commandType = 'command'; @@ -811,7 +658,7 @@ export class Curtain { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -826,7 +673,7 @@ export class Curtain { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`, + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, ); } } @@ -835,50 +682,53 @@ export class Curtain { * Handle requests to set the value of the "Target Position" characteristic */ async TargetPositionSet(value: CharacteristicValue): Promise { - if (this.TargetPosition === this.accessory.context.TargetPosition) { + if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); } // Set HoldPosition to false when TargetPosition is changed - this.HoldPosition = false; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition); + this.WindowCovering.HoldPosition = false; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition); - this.TargetPosition = value; + this.WindowCovering.TargetPosition = value; if (this.device.mqttURL) { - this.mqttPublish('TargetPosition', this.TargetPosition); - this.mqttPublish('HoldPosition', this.HoldPosition); + this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString()); + this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString()); // Convert boolean to string } await this.setMinMax(); - if (value > this.CurrentPosition) { - this.PositionState = this.hap.Characteristic.PositionState.INCREASING; + if (value > this.WindowCovering.CurrentPosition) { + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; this.setNewTarget = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`); - } else if (value < this.CurrentPosition) { - this.PositionState = this.hap.Characteristic.PositionState.DECREASING; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + } else if (value < this.WindowCovering.CurrentPosition) { + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; this.setNewTarget = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else { - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; + this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.setNewTarget = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState); - this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState); + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); + this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); /** * If Curtain movement time is short, the moving flag from backend is always false. * The minimum time depends on the network control latency. */ clearTimeout(this.setNewTargetTimer); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`); if (this.setNewTarget) { this.setNewTargetTimer = setTimeout(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`); this.setNewTarget = false; - }, this.updateRate * 1000); + }, this.deviceUpdateRate * 1000); } this.doCurtainUpdate.next(); } @@ -888,153 +738,98 @@ export class Curtain { */ async HoldPositionSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${value}`); - this.HoldPosition = value; + this.WindowCovering.HoldPosition = value; this.doCurtainUpdate.next(); } async updateHomeKitCharacteristics(): Promise { await this.setMinMax(); - if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`); + if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else { + this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); if (this.device.mqttURL) { - this.mqttPublish('CurrentPosition', this.CurrentPosition); + this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); // Convert to string } - this.accessory.context.CurrentPosition = this.CurrentPosition; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.CurrentPosition)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`); } - if (this.PositionState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`); + if (this.WindowCovering.PositionState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`); } else { if (this.device.mqttURL) { - this.mqttPublish('PositionState', this.PositionState); + this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString()); } - this.accessory.context.PositionState = this.PositionState; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.PositionState)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`); + this.accessory.context.PositionState = this.WindowCovering.PositionState; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` PositionState: ${this.WindowCovering.PositionState}`); } - if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`); + if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`); } else { if (this.device.mqttURL) { - this.mqttPublish('TargetPosition', this.TargetPosition); + this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString()); } - this.accessory.context.TargetPosition = this.TargetPosition; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.TargetPosition)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`); + this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}`); } - if (this.HoldPosition === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.HoldPosition}`); + if (this.WindowCovering.HoldPosition === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.WindowCovering.HoldPosition}`); } else { if (this.device.mqttURL) { - this.mqttPublish('HoldPosition', this.HoldPosition); + this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString()); } - this.accessory.context.HoldPosition = this.HoldPosition; - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic HoldPosition: ${this.HoldPosition}`); + this.accessory.context.HoldPosition = this.WindowCovering.HoldPosition; + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` HoldPosition: ${this.WindowCovering.HoldPosition}`); } if (!this.device.curtain?.hide_lightsensor) { - if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { if (this.device.mqttURL) { - this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel); + this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString()); } - this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel; - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); if (this.device.history) { this.historyService?.addEntry({ time: Math.round(new Date().valueOf() / 1000), - lux: this.CurrentAmbientLightLevel, + lux: this.LightSensor!.CurrentAmbientLightLevel, }); } } } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { if (this.device.mqttURL) { - this.mqttPublish('BatteryLevel', this.BatteryLevel); + this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString()); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { if (this.device.mqttURL) { - this.mqttPublish('StatusLowBattery', this.StatusLowBattery); - } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); - } - } - - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(topic: string, message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString()); } - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'c', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } @@ -1054,387 +849,100 @@ export class Curtain { } } - async SilentPerformance() { - if (Number(this.TargetPosition) > 50) { - if (this.device.curtain?.setOpenMode === '1') { - this.setPositionMode = '1'; - this.Mode = 'Silent Mode'; + async setPerformance() { + let setPositionMode: number; + let Mode: string; + if (Number(this.WindowCovering.TargetPosition) > 50) { + if (this.device.blindTilt?.setOpenMode === '1') { + setPositionMode = 1; + Mode = 'Silent Mode'; + } else if (this.device.blindTilt?.setOpenMode === '0') { + setPositionMode = 0; + Mode = 'Performance Mode'; } else { - this.setPositionMode = '0'; - this.Mode = 'Performance Mode'; + setPositionMode = 0; + Mode = 'Default Mode'; } } else { - if (this.device.curtain?.setCloseMode === '1') { - this.setPositionMode = '1'; - this.Mode = 'Silent Mode'; + if (this.device.blindTilt?.setCloseMode === '1') { + setPositionMode = 1; + Mode = 'Silent Mode'; + } else if (this.device.blindTilt?.setOpenMode === '0') { + setPositionMode = 0; + Mode = 'Performance Mode'; } else { - this.setPositionMode = '0'; - this.Mode = 'Performance Mode'; + setPositionMode = 0; + Mode = 'Default Mode'; } } + return { setPositionMode, Mode }; } async setMinMax(): Promise { if (this.device.curtain?.set_min) { - if (Number(this.CurrentPosition) <= this.device.curtain?.set_min) { - this.CurrentPosition = 0; + if (Number(this.WindowCovering.CurrentPosition) <= this.device.curtain?.set_min) { + this.WindowCovering.CurrentPosition = 0; } } if (this.device.curtain?.set_max) { - if (Number(this.CurrentPosition) >= this.device.curtain?.set_max) { - this.CurrentPosition = 100; + if (Number(this.WindowCovering.CurrentPosition) >= this.device.curtain?.set_max) { + this.WindowCovering.CurrentPosition = 100; } } if (this.device.history) { const motion = this.accessory.getService(this.hap.Service.MotionSensor); - const state = Number(this.CurrentPosition) > 0 ? 1 : 0; + const state = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0; motion?.updateCharacteristic(this.hap.Characteristic.MotionDetected, state); } } minStep(device: device & devicesConfig): number { + let set_minStep: number; if (device.curtain?.set_minStep) { - this.set_minStep = device.curtain?.set_minStep; + set_minStep = device.curtain?.set_minStep; } else { - this.set_minStep = 1; + set_minStep = 1; } - return this.set_minStep; + return set_minStep; } - minLux(): number { + async minLux(): Promise { + let set_minLux: number; if (this.device.curtain?.set_minLux) { - this.set_minLux = this.device.curtain?.set_minLux; + set_minLux = this.device.curtain?.set_minLux; } else { - this.set_minLux = 1; + set_minLux = 1; } - return this.set_minLux; + return set_minLux; } - maxLux(): number { + async maxLux(): Promise { + let set_maxLux: number; if (this.device.curtain?.set_maxLux) { - this.set_maxLux = this.device.curtain?.set_maxLux; - } else { - this.set_maxLux = 6001; - } - return this.set_maxLux; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - if (this.updateRate > device.scanDuration) { - this.scanDuration = this.updateRate; - if (this.BLE) { - this.warnLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`, - ); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - } - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } + set_maxLux = this.device.curtain?.set_maxLux; } else { - if (this.updateRate > 1) { - this.scanDuration = this.updateRate; - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - } - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_maxLux = 6001; } + return set_maxLux; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } } async apiError(e: any): Promise { - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, e); - this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); if (!this.device.curtain?.hide_lightsensor) { - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); } - - async deviceContext() { - if (this.accessory.context.CurrentPosition === undefined) { - this.CurrentPosition = 0; - } else { - this.CurrentPosition = this.accessory.context.CurrentPosition; - } - - if (this.accessory.context.TargetPosition === undefined) { - this.TargetPosition = 0; - } else { - this.TargetPosition = this.accessory.context.TargetPosition; - } - - if (this.accessory.context.PositionState === undefined) { - this.PositionState = this.hap.Characteristic.PositionState.STOPPED; - } else { - this.PositionState = this.accessory.context.PositionState; - } - - if (!this.device.curtain?.hide_lightsensor) { - if (this.accessory.context.CurrentAmbientLightLevel !== undefined) { - this.CurrentAmbientLightLevel = this.accessory.context.CurrentAmbientLightLevel; - } - } - - if (this.BLE) { - if (this.accessory.context.BatteryLevel === undefined) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; - } - if (this.accessory.context.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - } - - if (this.device.history === true) { - // initialize when this accessory is newly created. - this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0; - } else { - // removes cached values if history is turned off - delete this.accessory.context.lastActivation; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - // refreshRate - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - // updateRate - if (device?.curtain?.updateRate) { - this.updateRate = device?.curtain?.updateRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain updateRate: ${this.updateRate}`); - } else { - this.updateRate = 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain updateRate: ${this.updateRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.curtain) { - config = device.curtain; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/device/device.ts b/src/device/device.ts new file mode 100644 index 00000000..a07b97b8 --- /dev/null +++ b/src/device/device.ts @@ -0,0 +1,655 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * device.ts: @switchbot/homebridge-switchbot. + */ +import { API, HAP, Logging, PlatformAccessory } from 'homebridge'; + +import { hostname } from 'os'; +import { MqttClient } from 'mqtt'; +import asyncmqtt from 'async-mqtt'; +import { SwitchBotPlatform } from '../platform.js'; +import { SwitchBotPlatformConfig, device, devicesConfig } from '../settings.js'; +import { BlindTiltMappingMode, SwitchBotModel, SwitchBotBLEModel, sleep } from '../utils.js'; + +export abstract class deviceBase { + public readonly api: API; + public readonly log: Logging; + public readonly config!: SwitchBotPlatformConfig; + protected readonly hap: HAP; + + // Config + protected deviceLogging!: string; + protected deviceUpdateRate!: number; + protected deviceRefreshRate!: number; + protected deviceMaxRetries!: number; + protected deviceDelayBetweenRetries!: number; + + // Connection + protected readonly BLE: boolean; + protected readonly OpenAPI: boolean; + + // Accsrroy Information + protected deviceModel!: SwitchBotModel; + protected deviceBLEModel!: SwitchBotBLEModel; + + // BLE + protected scanDuration!: number; + + // EVE history service handler + protected historyService?: any = null; + + //MQTT stuff + protected mqttClient: MqttClient | null = null; + + constructor( + protected readonly platform: SwitchBotPlatform, + protected accessory: PlatformAccessory, + protected device: device & devicesConfig, + ) { + this.api = this.platform.api; + this.log = this.platform.log; + this.config = this.platform.config; + this.hap = this.api.hap; + + // Connection + this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; + this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + + this.getDeviceLogSettings(device); + this.getDeviceRefreshRateSettings(device); + this.getDeviceRetry(device); + this.getDeviceConfigSettings(device); + this.getDeviceContext(accessory, device); + this.setupMqtt(device); + this.scan(device); + + // Set accessory information + accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) + .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(accessory.context.FirmwareRevision); + } + + async getDeviceLogSettings(device: device & devicesConfig): Promise { + if (this.platform.debugMode) { + this.deviceLogging = this.accessory.context.logging = 'debugMode'; + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); + } else if (device.logging) { + this.deviceLogging = this.accessory.context.logging = device.logging; + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); + } else if (this.config.logging) { + this.deviceLogging = this.accessory.context.logging = this.config.logging; + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); + } else { + this.deviceLogging = this.accessory.context.logging = 'standard'; + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); + } + } + + async getDeviceRefreshRateSettings(device: device & devicesConfig): Promise { + // refreshRate + if (device.refreshRate) { + this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); + } else if (this.platform.config.options!.refreshRate) { + this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); + } + // updateRate + if (device.updateRate) { + this.deviceUpdateRate = device.updateRate; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config updateRate: ${this.deviceUpdateRate}`); + } else { + this.deviceUpdateRate = 5; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default updateRate: ${this.deviceUpdateRate}`); + } + } + + async getDeviceRetry(device: device & devicesConfig): Promise { + if (device.maxRetries) { + this.deviceMaxRetries = device.maxRetries; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.deviceMaxRetries}`); + } else { + this.deviceMaxRetries = 5; // Maximum number of retries + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.deviceMaxRetries}`); + } + if (device.delayBetweenRetries) { + this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`); + } else { + this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set,` + + ` Using: ${this.deviceDelayBetweenRetries}`); + } + } + + async retryBLE({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { + return fn().catch(async (e: any) => { + if (max === 0) { + throw e; + } + this.infoLog(e); + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); + await sleep(1000); + return this.retryBLE({ max: max - 1, fn }); + }); + } + + async maxRetryBLE(): Promise { + if (this.device.maxRetry) { + return this.device.maxRetry; + } else { + return 5; + } + } + + + async scan(device: device & devicesConfig): Promise { + if (device.scanDuration) { + if (this.deviceUpdateRate > device.scanDuration) { + this.scanDuration = this.deviceUpdateRate; + if (this.BLE) { + this.warnLog( + `${this.device.deviceType}: ` + + `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`, + ); + } + } else { + this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; + } + if (this.BLE) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); + } + } else { + if (this.deviceUpdateRate > 1) { + this.scanDuration = this.deviceUpdateRate; + } else { + this.scanDuration = this.accessory.context.scanDuration = 1; + } + if (this.BLE) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); + } + } + } + + async getDeviceConfigSettings(device: device & devicesConfig): Promise { + let config = {}; + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.refreshRate !== undefined) { + config['refreshRate'] = device.refreshRate; + } + if (device.updateRate !== undefined) { + config['updateRate'] = device.updateRate; + } + if (device.scanDuration !== undefined) { + config['scanDuration'] = device.scanDuration; + } + if (device.offline !== undefined) { + config['offline'] = device.offline; + } + if (device.maxRetry !== undefined) { + config['maxRetry'] = device.maxRetry; + } + if (device.webhook !== undefined) { + config['webhook'] = device.webhook; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.mqttURL !== undefined) { + config['mqttURL'] = device.mqttURL; + } + if (device.maxRetries !== undefined) { + config['maxRetries'] = device.maxRetries; + } + if (device.delayBetweenRetries !== undefined) { + config['delayBetweenRetries'] = device.delayBetweenRetries; + } + if (device.bot) { + config = device.bot; + } + if (device.lock) { + config = device.lock; + } + if (device.ceilinglight) { + config = device.ceilinglight; + } + if (device.colorbulb) { + config = device.colorbulb; + } + if (device.contact) { + config = device.contact; + } + if (device.motion) { + config = device.motion; + } + if (device.curtain) { + config = device.curtain; + } + if (device.hub) { + config = device.hub; + } + if (device.waterdetector) { + config = device.waterdetector; + } + if (device.humidifier) { + config = device.humidifier; + } + if (device.meter) { + config = device.meter; + } + if (device.striplight) { + config = device.striplight; + } + if (device.plug) { + config = device.plug; + } + if (device.blindTilt) { + if (device.blindTilt?.mode === undefined) { + config['mode'] = BlindTiltMappingMode.OnlyUp; + } else { + config['mode'] = device.blindTilt?.mode; + + config = device.blindTilt; + } + } + if (Object.entries(config).length !== 0) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + } + } + + /* + * Publish MQTT message for topics of + * 'homebridge-switchbot/${this.device.deviceType}/xx:xx:xx:xx:xx:xx' + */ + mqttPublish(message: string, topic?: string) { + const mac = this.device.deviceId + ?.toLowerCase() + .match(/[\s\S]{1,2}/g) + ?.join(':'); + const options = this.device.mqttPubOptions || {}; + let mqttTopic: string; + let mqttMessageTopic: string; + if (topic) { + mqttTopic = `/${topic}`; + mqttMessageTopic = `${topic}/`; + } else { + mqttTopic = ''; + mqttMessageTopic = ''; + } + this.mqttClient?.publish(`homebridge-switchbot/${this.device.deviceType}/${mac}${mqttTopic}`, `${message}`, options); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` MQTT message: ${mqttMessageTopic}${message} options:${JSON.stringify(options)}`); + } + + /* + * Setup MQTT hadler if URL is specified. + */ + async setupMqtt(device: device & devicesConfig): Promise { + if (device.mqttURL) { + try { + const { connectAsync } = asyncmqtt; + this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); + this.mqttClient.on('error', (e: Error) => { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); + }); + } catch (e) { + this.mqttClient = null; + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + } + } + } + + /* + * Setup EVE history graph feature if enabled. + */ + async setupHistoryService(device: device & devicesConfig): Promise { + const mac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); + this.historyService = device.history + ? new this.platform.fakegatoAPI('room', this.accessory, { + log: this.platform.log, + storage: 'fs', + filename: `${hostname().split('.')[0]}_${mac}_persist.json`, + }) + : null; + } + + async getCustomBLEAddress(switchbot: any) { + if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`); + (async () => { + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel }); + // Set an event handler + switchbot.onadvertisement = (ad: any) => { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); + }; + await sleep(10000); + // Stop to monitor + switchbot.stopScan(); + })(); + } + } + + async getDeviceContext(accessory: PlatformAccessory, device: device & devicesConfig): Promise { + // Set the accessory context + switch (device.deviceType) { + case 'Humidifier': + device.model = SwitchBotModel.Humidifier; + device.bleModel = SwitchBotBLEModel.Humidifier; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Hub Mini': + device.model = SwitchBotModel.HubMini; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Hub Plus': + device.model = SwitchBotModel.HubPlus; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Hub 2': + device.model = SwitchBotModel.Hub2; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Bot': + device.model = SwitchBotModel.Bot; + device.bleModel = SwitchBotBLEModel.Bot; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Meter': + device.model = SwitchBotModel.Meter; + device.bleModel = SwitchBotBLEModel.Meter; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'MeterPlus': + device.model = SwitchBotModel.MeterPlusUS; + device.bleModel = SwitchBotBLEModel.MeterPlus; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Meter Plus (JP)': + device.model = SwitchBotModel.MeterPlusJP; + device.bleModel = SwitchBotBLEModel.MeterPlus; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'WoIOSensor': + device.model = SwitchBotModel.OutdoorMeter; + device.bleModel = SwitchBotBLEModel.OutdoorMeter; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Water Detector': + device.model = SwitchBotModel.WaterDetector; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Motion Sensor': + device.model = SwitchBotModel.MotionSensor; + device.bleModel = SwitchBotBLEModel.MotionSensor; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Contact Sensor': + device.model = SwitchBotModel.ContactSensor; + device.bleModel = SwitchBotBLEModel.ContactSensor; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Curtain': + device.model = SwitchBotModel.Curtain; + device.bleModel = SwitchBotBLEModel.Curtain; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Curtain3': + device.model = SwitchBotModel.Curtain3; + device.bleModel = SwitchBotBLEModel.Curtain3; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Blind Tilt': + device.model = SwitchBotModel.BlindTilt; + device.bleModel = SwitchBotBLEModel.BlindTilt; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Plug': + device.model = SwitchBotModel.Plug; + device.bleModel = SwitchBotBLEModel.PlugMiniUS; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Plug Mini (US)': + device.model = SwitchBotModel.PlugMiniUS; + device.bleModel = SwitchBotBLEModel.PlugMiniUS; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Plug Mini (JP)': + device.model = SwitchBotModel.PlugMiniJP; + device.bleModel = SwitchBotBLEModel.PlugMiniJP; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Smart Lock': + device.model = SwitchBotModel.Lock; + device.bleModel = SwitchBotBLEModel.Lock; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Smart Lock Pro': + device.model = SwitchBotModel.LockPro; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Color Bulb': + device.model = SwitchBotModel.ColorBulb; + device.bleModel = SwitchBotBLEModel.ColorBulb; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'K10+': + device.model = SwitchBotModel.K10; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'WoSweeper': + device.model = SwitchBotModel.WoSweeper; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'WoSweeperMini': + device.model = SwitchBotModel.WoSweeperMini; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Robot Vacuum Cleaner S1': + device.model = SwitchBotModel.RobotVacuumCleanerS1; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Robot Vacuum Cleaner S1 Plus': + device.model = SwitchBotModel.RobotVacuumCleanerS1Plus; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Ceiling Light': + device.model = SwitchBotModel.CeilingLight; + device.bleModel = SwitchBotBLEModel.CeilingLight; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Ceiling Light Pro': + device.model = SwitchBotModel.CeilingLightPro; + device.bleModel = SwitchBotBLEModel.CeilingLightPro; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Strip Light': + device.model = SwitchBotModel.StripLight; + device.bleModel = SwitchBotBLEModel.StripLight; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Indoor Cam': + device.model = SwitchBotModel.IndoorCam; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'Remote': + device.model = SwitchBotModel.Remote; + device.bleModel = SwitchBotBLEModel.Remote; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + case 'remote with screen+': + device.model = SwitchBotModel.UniversalRemote; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; + default: + device.model = SwitchBotModel.Unknown; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + } + accessory.context.name = device.deviceName; + accessory.context.model = device.model; + accessory.context.deviceId = device.deviceId; + accessory.context.deviceType = device.deviceType; + if (device.firmware === undefined) { + device.firmware = this.platform.version; + accessory.context.FirmwareRevision = device.firmware; + } else { + accessory.context.FirmwareRevision = device.firmware; + } + } + + async statusCode(statusCode: number): Promise { + switch (statusCode) { + case 151: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); + break; + case 152: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); + break; + case 160: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); + break; + case 161: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); + break; + case 171: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + + `Hub: ${this.device.hubDeviceId}`); + break; + case 190: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + + ` server, Or command format is invalid, statusCode: ${statusCode}`); + break; + case 100: + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); + break; + case 200: + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); + break; + case 400: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` + + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); + break; + case 401: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` + + `but the request has not been authenticated, statusCode: ${statusCode}`); + break; + case 403: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` + + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); + break; + case 404: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` + + `statusCode: ${statusCode}`); + break; + case 406: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` + + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); + break; + case 415: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` + + `header that is not supported by the server, statusCode: ${statusCode}`); + break; + case 422: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); + break; + case 429: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` + + `requests allowed for a given time window, statusCode: ${statusCode}`); + break; + case 500: + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` + + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); + break; + default: + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + } + } + + /** + * Logging for Device + */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + + infoLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.info(String(...log)); + } + } + + warnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.warn(String(...log)); + } + } + + debugWarnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.log.warn('[DEBUG]', String(...log)); + } + } + } + + errorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.error(String(...log)); + } + } + + debugErrorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.log.error('[DEBUG]', String(...log)); + } + } + } + + debugLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging === 'debug') { + this.log.info('[DEBUG]', String(...log)); + } else { + this.log.debug(String(...log)); + } + } + } + + enablingDeviceLogging(): boolean { + return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + } +} \ No newline at end of file diff --git a/src/device/hub.ts b/src/device/hub.ts index c9f2815d..daf50c75 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -1,108 +1,83 @@ -import asyncmqtt from 'async-mqtt'; -import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, HAP } from 'homebridge'; -import { MqttClient } from 'mqtt'; -import { hostname } from 'os'; -import { interval } from 'rxjs'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * hub.ts: @switchbot/homebridge-switchbot. + */ +import { Subject, interval, skipWhile } from 'rxjs'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, device, deviceStatus, devicesConfig, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, device, deviceStatus, devicesConfig } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; -export class Hub { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Hub extends deviceBase { // Services - lightSensorService?: Service; - humidityService?: Service; - temperatureService?: Service; - - // Characteristic Values - FirmwareRevision!: CharacteristicValue; - CurrentTemperature!: CharacteristicValue; - CurrentRelativeHumidity!: CharacteristicValue; - CurrentAmbientLightLevel!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; - OpenAPI_CurrentAmbientLightLevel!: deviceStatus['brightness']; - - // OpenAPI Others - spaceBetweenLevels!: number; - - //MQTT stuff - mqttClient: MqttClient | null = null; - - // EVE history service handler - historyService?: any; - - // Config - set_minStep!: number; - updateRate!: number; - set_minLux!: number; - set_maxLux!: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; - - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; + private LightSensor?: { + Service: Service; + CurrentAmbientLightLevel: CharacteristicValue; + }; + + private HumiditySensor?: { + Service: Service; + CurrentRelativeHumidity: CharacteristicValue; + }; + + private TemperatureSensor?: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; + + // Updates + hubUpdateInProgress!: boolean; + doHubUpdate!: Subject; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.refreshRate(device); - this.deviceContext(); - this.setupHistoryService(device); - this.setupMqtt(device); - this.deviceRetry(device); - this.deviceConfig(device); - - this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity; - this.CurrentTemperature = accessory.context.CurrentTemperature; - + super(platform, accessory, device); + // this is subject we use to track when we need to POST changes to the SwitchBot API + this.doHubUpdate = new Subject(); + this.hubUpdateInProgress = false; // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Temperature Sensor property + if (!device.hub?.hide_temperature) { + this.TemperatureSensor = { + Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + CurrentTemperature: accessory.context.CurrentTemperature || 0, + }; + } + + // Initialize Humidity Sensor property + if (!device.hub?.hide_humidity) { + this.HumiditySensor = { + Service: this.accessory.addService(this.hap.Service.HumiditySensor), + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 0, + }; + } + + // Initialize Light Sensor property + if (!device.hub?.hide_lightsensor) { + this.LightSensor = { + Service: this.accessory.addService(this.hap.Service.LightSensor), + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0, + }; + } // Temperature Sensor Service if (device.hub?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureService!); - } else if (!this.temperatureService) { + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } else if (!this.TemperatureSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureService = `${accessory.displayName} Temperature Sensor`; - (this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureService; + const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; + (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - this.temperatureService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureService + this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); + this.TemperatureSensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -112,7 +87,7 @@ export class Hub { minStep: 0.1, }) .onGet(() => { - return this.CurrentTemperature!; + return this.TemperatureSensor!.CurrentTemperature; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); @@ -121,25 +96,22 @@ export class Hub { // Humidity Sensor Service if (device.hub?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor); - accessory.removeService(this.humidityService!); - } else if (!this.humidityService) { + this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor!.Service); + } else if (!this.HumiditySensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const humidityService = `${accessory.displayName} Humidity Sensor`; - (this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityService; + const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; + (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) + || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; - this.humidityService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); - if (!this.humidityService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidityService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); - } - this.humidityService + this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + this.HumiditySensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, }) .onGet(() => { - return this.CurrentRelativeHumidity; + return this.HumiditySensor!.CurrentRelativeHumidity; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); @@ -148,16 +120,15 @@ export class Hub { // Light Sensor Service if (device.hub?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor); - accessory.removeService(this.lightSensorService!); - } else if (!this.lightSensorService) { + this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor!.Service); + } else if (!this.LightSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const lightSensorService = `${accessory.displayName} Light Sensor`; - (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService; + const LightSensorService = `${accessory.displayName} Light Sensor`; + (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) + || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`); - this.lightSensorService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`); + this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); } @@ -167,6 +138,7 @@ export class Hub { // Start an update interval interval(this.deviceRefreshRate * 1000) + .pipe(skipWhile(() => this.hubUpdateInProgress)) .subscribe(async () => { await this.refreshStatus(); }); @@ -179,100 +151,24 @@ export class Hub { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); if (context.scale === 'CELSIUS') { const { temperature, humidity, lightLevel } = context; - const { CurrentTemperature, CurrentRelativeHumidity, CurrentAmbientLightLevel } = this; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity, lightLevel) = ' + `Webhook:(${temperature}, ${humidity}, ${lightLevel}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - this.spaceBetweenLevels = 19; - switch (lightLevel) { - case 1: - this.CurrentAmbientLightLevel = this.set_minLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 2: - this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` + - ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`, - ); - break; - case 3: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 4: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 5: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 6: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 7: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 8: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 9: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 10: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 9; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 11: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 10; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 12: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 11; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 13: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 12; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 14: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 13; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 15: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 14; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 16: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 15; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 17: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 16; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 18: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 17; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 19: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 18; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 20: - default: - this.CurrentAmbientLightLevel = this.set_maxLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); + if (!this.device.hub?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (!this.device.hub?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } + if (!this.device.hub?.hide_lightsensor) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + const spaceBetweenLevels = 19; + await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); } this.updateHomeKitCharacteristics(); } @@ -284,140 +180,40 @@ export class Hub { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE not supported`); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // CurrentRelativeHumidity if (!this.device.hub?.hide_humidity) { - this.CurrentRelativeHumidity = Number(this.OpenAPI_CurrentRelativeHumidity); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + this.HumiditySensor!.CurrentRelativeHumidity = Number(deviceStatus.body.humidity); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // CurrentTemperature if (!this.device.hub?.hide_temperature) { - this.CurrentTemperature = Number(this.OpenAPI_CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + this.TemperatureSensor!.CurrentTemperature = Number(deviceStatus.body.temperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } // Brightness if (!this.device.hub?.hide_lightsensor) { if (!this.device.curtain?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - this.spaceBetweenLevels = 19; - switch (this.OpenAPI_CurrentAmbientLightLevel) { - case 1: - this.CurrentAmbientLightLevel = this.set_minLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 2: - this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` + - ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`, - ); - break; - case 3: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 4: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 5: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 6: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 7: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 8: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 9: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 10: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 9; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 11: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 10; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 12: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 11; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 13: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 12; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 14: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 13; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 15: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 14; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 16: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 15; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 17: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 16; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 18: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 17; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 19: - this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 18; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - break; - case 20: - default: - this.CurrentAmbientLightLevel = this.set_maxLux; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`); - } + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + const spaceBetweenLevels = 19; + const lightLevel = deviceStatus.body.lightLevel; + await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` + - ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${deviceStatus.body.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } if (!this.device.hub?.hide_lightsensor) { - this.lightSensorService?.setCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); } } // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } async refreshStatus(): Promise { @@ -434,7 +230,7 @@ export class Hub { async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders(), }); @@ -445,11 +241,8 @@ export class Hub { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature; - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity; - this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.lightLevel; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -474,55 +267,59 @@ export class Hub { // CurrentRelativeHumidity if (!this.device.hub?.hide_humidity) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + +` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); + mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`); } if (this.device.history) { - entry['humidity'] = this.CurrentRelativeHumidity; + entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity; } - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); + this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumiditySensor!.CurrentRelativeHumidity); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + + `updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } } // CurrentTemperature if (!this.device.hub?.hide_temperature) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + if (this.TemperatureSensor!.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); + mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`); } if (this.device.history) { - entry['temp'] = this.CurrentTemperature; + entry['temp'] = this.TemperatureSensor!.CurrentTemperature; } - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } } // CurrentAmbientLightLevel if (!this.device.hub?.hide_lightsensor) { - if (this.CurrentAmbientLightLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"light": ${this.CurrentAmbientLightLevel}`); + mqttmessage.push(`"light": ${this.LightSensor!.CurrentAmbientLightLevel}`); } if (this.device.history) { - entry['lux'] = this.CurrentAmbientLightLevel; + entry['lux'] = this.LightSensor!.CurrentAmbientLightLevel; } - this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel; - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} ` - + `updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + + `updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } } @@ -531,7 +328,7 @@ export class Hub { if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.CurrentRelativeHumidity) > 0) { + if (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0) { // reject unreliable data if (this.device.history) { this.historyService?.addEntry(entry); @@ -539,324 +336,137 @@ export class Hub { } } - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/hub/${mac}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); + async offlineOff(): Promise { + if (this.device.offline) { + if (!this.device.hub?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.accessory.context.CurrentTemperature); + } + if (!this.device.hub?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.accessory.context.CurrentRelativeHumidity); + } + if (!this.device.hub?.hide_lightsensor) { + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, + this.accessory.context.CurrentAmbientLightLevel); + } + } } - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); - } + async apiError(e: any): Promise { + if (!this.device.hub?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); + } + if (!this.device.hub?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + } + if (!this.device.hub?.hide_lightsensor) { + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); } } - /* - * Setup EVE history graph feature if enabled. - */ - async setupHistoryService(device: device & devicesConfig): Promise { - const mac = this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); - this.historyService = device.history - ? new this.platform.fakegatoAPI('custom', this.accessory, { - log: this.platform.log, - storage: 'fs', - filename: `${hostname().split('.')[0]}_${mac}_persist.json`, - }) - : null; + async minLux(): Promise { + let set_minLux: number; + if (this.device.curtain?.set_minLux) { + set_minLux = this.device.curtain?.set_minLux; + } else { + set_minLux = 1; + } + return set_minLux; + } + + async maxLux(): Promise { + let set_maxLux: number; + if (this.device.curtain?.set_maxLux) { + set_maxLux = this.device.curtain?.set_maxLux; + } else { + set_maxLux = 6001; + } + return set_maxLux; } - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); + async getLightLevel(lightLevel: any, set_minLux: number, set_maxLux: number, spaceBetweenLevels: number) { + switch (lightLevel) { + case 1: + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); + case 2: + this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`); break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); + case 3: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); + case 4: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); + case 5: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); + case 6: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); + break; + case 7: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); + case 8: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); + case 9: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); + case 10: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 9; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); + case 11: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 10; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); + case 12: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 11; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); + case 13: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 12; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); + case 14: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 13; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); + case 15: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 14; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); + case 16: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 15; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); + case 17: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 16; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); + case 18: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 17; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); break; + case 19: + this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 18; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); + break; + case 20: default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - - async offlineOff(): Promise { - if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); - } - } - - async apiError(e: any): Promise { - if (!this.device.hub?.hide_temperature) { - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } - if (!this.device.hub?.hide_humidity) { - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); - } - if (!this.device.hub?.hide_lightsensor) { - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); - } - } - - minLux(): number { - if (this.device.curtain?.set_minLux) { - this.set_minLux = this.device.curtain?.set_minLux; - } else { - this.set_minLux = 1; - } - return this.set_minLux; - } - - maxLux(): number { - if (this.device.curtain?.set_maxLux) { - this.set_maxLux = this.device.curtain?.set_maxLux; - } else { - this.set_maxLux = 6001; - } - return this.set_maxLux; - } - - async deviceContext() { - 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; - } - if (this.CurrentAmbientLightLevel === undefined) { - this.CurrentAmbientLightLevel = this.set_minLux; - } else { - this.CurrentAmbientLightLevel = this.accessory.context.CurrentAmbientLightLevel; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - // refreshRate - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - // updateRate - if (device?.curtain?.updateRate) { - this.updateRate = device?.curtain?.updateRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain updateRate: ${this.updateRate}`); - } else { - this.updateRate = 7; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain updateRate: ${this.updateRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.hub) { - config = device.hub; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`); } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; } } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 8cc55062..90f864ae 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -1,86 +1,43 @@ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Humidifier { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Humidifier extends deviceBase { // Services - humidifierService: Service; - temperatureservice?: Service; - - // Characteristic Values - Active!: CharacteristicValue; - WaterLevel!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - CurrentTemperature!: CharacteristicValue; - CurrentRelativeHumidity!: CharacteristicValue; - TargetHumidifierDehumidifierState!: CharacteristicValue; - CurrentHumidifierDehumidifierState!: CharacteristicValue; - RelativeHumidityHumidifierThreshold!: CharacteristicValue; - - // OpenAPI - OpenAPI_Active: deviceStatus['power']; - OpenAPI_WaterLevel: deviceStatus['lackWater']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; - OpenAPI_CurrentHumidifierDehumidifierState: deviceStatus['auto']; - OpenAPI_RelativeHumidityHumidifierThreshold: deviceStatus['nebulizationEfficiency']; - - // BLE Others - connected?: boolean; - onState!: serviceData['onState']; - autoMode!: serviceData['autoMode']; - percentage!: serviceData['percentage']; - - // Config - set_minStep?: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private HumidifierDehumidifier: { + Service: Service; + Active: CharacteristicValue; + WaterLevel: CharacteristicValue; + CurrentRelativeHumidity: CharacteristicValue; + TargetHumidifierDehumidifierState: CharacteristicValue; + CurrentHumidifierDehumidifierState: CharacteristicValue; + RelativeHumidityHumidifierThreshold: CharacteristicValue; + }; + + private TemperatureSensor?: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; // Updates humidifierUpdateInProgress!: boolean; doHumidifierUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doHumidifierUpdate = new Subject(); this.humidifierUpdateInProgress = false; @@ -88,35 +45,45 @@ export class Humidifier { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W0801800') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize the HumidifierDehumidifier Service + this.HumidifierDehumidifier = { + Service: accessory.addService(this.hap.Service.HumidifierDehumidifier), + Active: accessory.context.Active || this.hap.Characteristic.Active.ACTIVE, + WaterLevel: accessory.context.WaterLevel || 100, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, + TargetHumidifierDehumidifierState: accessory.context.TargetHumidifierDehumidifierState + || this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER, + CurrentHumidifierDehumidifierState: accessory.context.CurrentHumidifierDehumidifierState + || this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE, + RelativeHumidityHumidifierThreshold: accessory.context.RelativeHumidityHumidifierThreshold || 50, + }; + + // Initialize the Temperature Sensor Service + if (!device.humidifier?.hide_temperature) { + this.TemperatureSensor = { + Service: accessory.addService(this.hap.Service.TemperatureSensor), + CurrentTemperature: accessory.context.CurrentTemperature || 30, + }; + } // get the service if it exists, otherwise create a new service // you can create multiple services for each accessory - const humidifierService = `${accessory.displayName} Humidifier`; - (this.humidifierService = accessory.getService(this.hap.Service.HumidifierDehumidifier) - || accessory.addService(this.hap.Service.HumidifierDehumidifier)), humidifierService; + const HumidifierDehumidifierService = `${accessory.displayName} Humidifier`; + (this.HumidifierDehumidifier.Service = accessory.getService(this.hap.Service.HumidifierDehumidifier) + || accessory.addService(this.hap.Service.HumidifierDehumidifier)), HumidifierDehumidifierService; - this.humidifierService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.humidifierService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidifierService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.HumidifierDehumidifier.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/HumidifierDehumidifier // create handlers for required characteristics - this.humidifierService.setCharacteristic( + this.HumidifierDehumidifier.Service.setCharacteristic( this.hap.Characteristic.CurrentHumidifierDehumidifierState, - this.CurrentHumidifierDehumidifierState, + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState, ); - this.humidifierService + this.HumidifierDehumidifier.Service .getCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState) .setProps({ validValueRanges: [0, 1], @@ -126,34 +93,31 @@ export class Humidifier { }) .onSet(this.TargetHumidifierDehumidifierStateSet.bind(this)); - this.humidifierService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.HumidifierDehumidifier.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - this.humidifierService + this.HumidifierDehumidifier.Service .getCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold) .setProps({ validValueRanges: [0, 100], minValue: 0, maxValue: 100, - minStep: this.minStep(), + minStep: this.minStep(device), }) .onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this)); // Temperature Sensor Service if (device.humidifier?.hide_temperature || this.BLE) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureservice!); - } else if (!this.temperatureservice && !this.BLE) { + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } else if (!this.TemperatureSensor!.Service && !this.BLE) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureservice = `${accessory.displayName} Temperature Sensor`; - (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice; + const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; + (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureservice + this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); + this.TemperatureSensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ validValueRanges: [-273.15, 100], @@ -162,7 +126,7 @@ export class Humidifier { minStep: 0.1, }) .onGet(() => { - return this.CurrentTemperature; + return this.TemperatureSensor!.CurrentTemperature; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); @@ -186,13 +150,16 @@ export class Humidifier { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); if (context.scale === 'CELSIUS') { const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; + const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity) = ' + `Webhook:(${temperature}, ${humidity}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; + this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; + if (!this.device.humidifier?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } this.updateHomeKitCharacteristics(); } } catch (e: any) { @@ -225,105 +192,86 @@ export class Humidifier { }); } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); + // Target Humidifier Dehumidifier State + if (serviceData.autoMode) { + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; + } // Current Relative Humidity - this.CurrentRelativeHumidity = this.percentage!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.HumidifierDehumidifier.CurrentRelativeHumidity = serviceData.percentage!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`); // Active - if (this.onState) { - this.Active = this.hap.Characteristic.Active.ACTIVE; + if (serviceData.onState) { + this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE; } else { - this.Active = this.hap.Characteristic.Active.INACTIVE; + this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.INACTIVE; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // Current Relative Humidity - this.CurrentRelativeHumidity = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.HumidifierDehumidifier.CurrentRelativeHumidity = deviceStatus.body.temperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`); // Current Temperature if (!this.device.humidifier?.hide_temperature) { - this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } // Target Humidifier Dehumidifier State - switch (this.OpenAPI_CurrentHumidifierDehumidifierState) { + switch (deviceStatus.body.auto) { case true: - this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER; - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; - this.RelativeHumidityHumidifierThreshold = this.CurrentRelativeHumidity; + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = + this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER; + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; + this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = this.HumidifierDehumidifier.CurrentRelativeHumidity; break; default: - this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; - if (this.OpenAPI_RelativeHumidityHumidifierThreshold! > 100) { - this.RelativeHumidityHumidifierThreshold = 100; + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; + if (deviceStatus.body.nebulizationEfficiency! > 100) { + this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = 100; } else { - this.RelativeHumidityHumidifierThreshold = this.OpenAPI_RelativeHumidityHumidifierThreshold!; + this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = deviceStatus.body.nebulizationEfficiency!; } - if (this.CurrentRelativeHumidity > this.RelativeHumidityHumidifierThreshold) { - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE; - } else if (this.Active === this.hap.Characteristic.Active.INACTIVE) { - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; + if (this.HumidifierDehumidifier.CurrentRelativeHumidity > this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold) { + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE; + } else if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) { + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; } else { - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; } } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`, - ); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`, - ); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); // Active - switch (this.OpenAPI_Active) { + switch (deviceStatus.body.power) { case 'on': - this.Active = this.hap.Characteristic.Active.ACTIVE; + this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE; break; default: - this.Active = this.hap.Characteristic.Active.INACTIVE; + this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.INACTIVE; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`); // Water Level - if (this.OpenAPI_WaterLevel) { - this.WaterLevel = 0; + if (deviceStatus.body.lackWater) { + this.HumidifierDehumidifier.WaterLevel = 0; } else { - this.WaterLevel = 100; + this.HumidifierDehumidifier.WaterLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } - /** - * Asks the SwitchBot API for the latest device information - */ async refreshStatus(): Promise { if (!this.device.enableCloudService && this.OpenAPI) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`); @@ -353,89 +301,35 @@ export class Humidifier { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'e', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'e') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.autoMode = ad.serviceData.autoMode; - this.onState = ad.serviceData.onState; - this.percentage = ad.serviceData.percentage > 100 ? 100 : ad.serviceData.percentage; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'e', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.autoMode = ad.serviceData.autoMode; - this.onState = ad.serviceData.onState; - this.percentage = ad.serviceData.percentage; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` + - `autoMode: ${ad.serviceData.autoMode}, onState: ${ad.serviceData.onState}, percentage: ${ad.serviceData.percentage}`, - ); - - if (ad.serviceData) { - this.connected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`); - await this.stopScanning(switchbot); - } else { - this.connected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -443,14 +337,7 @@ export class Humidifier { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentHumidifierDehumidifierState = deviceStatus.body.auto; - this.OpenAPI_Active = deviceStatus.body.power; - this.OpenAPI_WaterLevel = deviceStatus.body.lackWater; - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity; - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature; - this.OpenAPI_RelativeHumidityHumidifierThreshold = deviceStatus.body.nebulizationEfficiency; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -504,13 +391,13 @@ export class Humidifier { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Active: ${this.Active}`); - return await device_list[0].percentage(this.RelativeHumidityHumidifierThreshold); + this.infoLog(`${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`); + return await device_list[0].percentage(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `Active: ${this.Active} sent over BLE, sent successfully`); + + `Active: ${this.HumidifierDehumidifier.Active} sent over BLE, sent successfully`); }) .catch(async (e: any) => { this.apiError(e); @@ -525,13 +412,14 @@ export class Humidifier { async openAPIpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); if ( - this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER && - this.Active === this.hap.Characteristic.Active.ACTIVE + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER && + this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE ) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Manual: ${this.RelativeHumidityHumidifierThreshold}!`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` Pushing Manual: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}!`); const bodyChange = JSON.stringify({ command: 'setMode', - parameter: `${this.RelativeHumidityHumidifierThreshold}`, + parameter: `${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`, commandType: 'command', }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); @@ -550,7 +438,7 @@ export class Humidifier { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -563,8 +451,9 @@ export class Humidifier { ); } } else if ( - this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && - this.Active === this.hap.Characteristic.Active.ACTIVE + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === + this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && + this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE ) { await this.pushAutoChanges(); } else { @@ -578,8 +467,9 @@ export class Humidifier { async pushAutoChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushAutoChanges`); if ( - this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && - this.Active === this.hap.Characteristic.Active.ACTIVE + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === + this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && + this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE ) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Auto`); const bodyChange = JSON.stringify({ @@ -603,7 +493,7 @@ export class Humidifier { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -616,10 +506,8 @@ export class Humidifier { ); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushAutoChanges.` + - `TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}, Active: ${this.Active}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushAutoChanges. TargetHumidifierDehumidifierState:` + + ` ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}, Active: ${this.HumidifierDehumidifier.Active}`); } } @@ -628,7 +516,7 @@ export class Humidifier { */ async pushActiveChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushActiveChanges`); - if (this.Active === this.hap.Characteristic.Active.INACTIVE) { + if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Off`); const bodyChange = JSON.stringify({ command: 'turnOff', @@ -651,7 +539,7 @@ export class Humidifier { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -664,7 +552,7 @@ export class Humidifier { ); } } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushActiveChanges. Active: ${this.Active}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushActiveChanges. Active: ${this.HumidifierDehumidifier.Active}`); } } @@ -672,13 +560,13 @@ export class Humidifier { * Handle requests to set the "Active" characteristic */ async ActiveSet(value: CharacteristicValue): Promise { - if (this.Active === this.accessory.context.Active) { + if (this.HumidifierDehumidifier.Active === this.accessory.context.Active) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Active: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); } - this.Active = value; + this.HumidifierDehumidifier.Active = value; this.doHumidifierUpdate.next(); } @@ -686,13 +574,13 @@ export class Humidifier { * Handle requests to set the "Target Humidifier Dehumidifier State" characteristic */ async TargetHumidifierDehumidifierStateSet(value: CharacteristicValue): Promise { - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHumidifierDehumidifierState: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHumidifierDehumidifierState: ${value}`); } - this.TargetHumidifierDehumidifierState = value; + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = value; this.doHumidifierUpdate.next(); } @@ -700,16 +588,16 @@ export class Humidifier { * Handle requests to set the "Relative Humidity Humidifier Threshold" characteristic */ async RelativeHumidityHumidifierThresholdSet(value: CharacteristicValue): Promise { - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RelativeHumidityHumidifierThreshold: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RelativeHumidityHumidifierThreshold: ${value}`); } - this.RelativeHumidityHumidifierThreshold = value; - if (this.Active === this.hap.Characteristic.Active.INACTIVE) { - this.Active = this.hap.Characteristic.Active.ACTIVE; - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE; + this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = value; + if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) { + this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE; + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE; } this.doHumidifierUpdate.next(); } @@ -718,117 +606,76 @@ export class Humidifier { * Updates the status for each of the HomeKit Characteristics */ async updateHomeKitCharacteristics(): Promise { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + if (this.HumidifierDehumidifier.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`); } else { - this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, - ); - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumidifierDehumidifier.CurrentRelativeHumidity); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`); + this.accessory.context.CurrentRelativeHumidity = this.HumidifierDehumidifier.CurrentRelativeHumidity; } if (this.OpenAPI) { - if (this.WaterLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); + if (this.HumidifierDehumidifier.WaterLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); } else { - this.humidifierService.updateCharacteristic(this.hap.Characteristic.WaterLevel, this.WaterLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic WaterLevel: ${this.WaterLevel}`); - this.accessory.context.WaterLevel = this.WaterLevel; + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.WaterLevel, this.HumidifierDehumidifier.WaterLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); + this.accessory.context.WaterLevel = this.HumidifierDehumidifier.WaterLevel; } } - if (this.CurrentHumidifierDehumidifierState === undefined) { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`, - ); + if (this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); } else { - this.humidifierService.updateCharacteristic( - this.hap.Characteristic.CurrentHumidifierDehumidifierState, - this.CurrentHumidifierDehumidifierState, - ); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`, - ); - this.accessory.context.CurrentHumidifierDehumidifierState = this.CurrentHumidifierDehumidifierState; - } - if (this.TargetHumidifierDehumidifierState === undefined) { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`, + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); + this.accessory.context.CurrentHumidifierDehumidifierState = this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState; + } + if (this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`, ); } else { - this.humidifierService.updateCharacteristic( - this.hap.Characteristic.TargetHumidifierDehumidifierState, - this.TargetHumidifierDehumidifierState, - ); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`, - ); - this.accessory.context.TargetHumidifierDehumidifierState = this.TargetHumidifierDehumidifierState; - } - if (this.Active === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, + this.HumidifierDehumidifier.TargetHumidifierDehumidifierState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`); + this.accessory.context.TargetHumidifierDehumidifierState = this.HumidifierDehumidifier.TargetHumidifierDehumidifierState; + } + if (this.HumidifierDehumidifier.Active === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`); } else { - this.humidifierService.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); - this.accessory.context.Active = this.Active; + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, this.HumidifierDehumidifier.Active); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.HumidifierDehumidifier.Active}`); + this.accessory.context.Active = this.HumidifierDehumidifier.Active; } - if (this.RelativeHumidityHumidifierThreshold === undefined) { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`, - ); + if (this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); } else { - this.humidifierService.updateCharacteristic( - this.hap.Characteristic.RelativeHumidityHumidifierThreshold, - this.RelativeHumidityHumidifierThreshold, - ); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`, - ); - this.accessory.context.RelativeHumidityHumidifierThreshold = this.RelativeHumidityHumidifierThreshold; + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, + this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); + this.accessory.context.RelativeHumidityHumidifierThreshold = this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; } if (!this.device.humidifier?.hide_temperature && !this.BLE) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + if (this.TemperatureSensor!.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } else { - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); - this.accessory.context.CurrentTemperature = this.CurrentTemperature; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); + this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; } } } - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.connected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'e', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLEPushConnection() { if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); @@ -845,306 +692,38 @@ export class Humidifier { } } - minStep(): number { - if (this.device.humidifier?.set_minStep) { - this.set_minStep = this.device.humidifier?.set_minStep; + minStep(device: device & devicesConfig): number { + let set_minStep: number; + if (device.humidifier?.set_minStep) { + set_minStep = device.humidifier?.set_minStep; } else { - this.set_minStep = 1; - } - return this.set_minStep; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_minStep = 1; } + return set_minStep; } async offlineOff(): Promise { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} offline: ${this.device.offline}`); if (this.device.offline) { - await this.deviceContext(); - if (this.CurrentTemperature === undefined) { - this.CurrentTemperature = 0; - } - await this.updateHomeKitCharacteristics(); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, + this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, + this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); } } async apiError(e: any): Promise { - this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); if (!this.BLE) { - this.humidifierService.updateCharacteristic(this.hap.Characteristic.WaterLevel, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.WaterLevel, e); } - this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, e); - this.humidifierService.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, e); - this.humidifierService.updateCharacteristic(this.hap.Characteristic.Active, e); - this.humidifierService.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, e); if (!this.device.humidifier?.hide_temperature && !this.BLE) { - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } - } - - async deviceContext() { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.ACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.CurrentTemperature === undefined) { - this.CurrentTemperature = 30; - } else { - this.CurrentTemperature = this.accessory.context.CurrentTemperature; - } - if (this.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; - } else { - this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity; - } - if (this.TargetHumidifierDehumidifierState === undefined) { - this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; - } else if (this.accessory.context.TargetHumidifierDehumidifierState === undefined) { - this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; - } else { - this.TargetHumidifierDehumidifierState = this.accessory.context.TargetHumidifierDehumidifierState; - } - if (this.CurrentHumidifierDehumidifierState === undefined) { - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; - } else if (this.accessory.context.CurrentHumidifierDehumidifierState === undefined) { - this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; - } else { - this.CurrentHumidifierDehumidifierState = this.accessory.context.CurrentHumidifierDehumidifierState; - } - if (this.RelativeHumidityHumidifierThreshold === undefined) { - this.RelativeHumidityHumidifierThreshold = 0; - } else { - this.RelativeHumidityHumidifierThreshold = this.accessory.context.RelativeHumidityHumidifierThreshold; - } - if (this.WaterLevel === undefined) { - this.WaterLevel = 0; - } else { - this.WaterLevel = this.accessory.context.WaterLevel; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.humidifier) { - config = device.humidifier; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - async infoLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); } } - - async warnLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - async debugWarnLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - async errorLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - async debugErrorLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - async debugLog(...log: any[]): Promise { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index cfa8ae54..2f1068b1 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -1,94 +1,46 @@ -import { hostname } from 'os'; -import { sleep } from '../utils.js'; -import { MqttClient } from 'mqtt'; -import asyncmqtt from 'async-mqtt'; -import { interval, Subject } from 'rxjs'; -import { skipWhile } from 'rxjs/operators'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * iosensor.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, Units, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, temperature, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Subject, interval, skipWhile } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class IOSensor { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class IOSensor extends deviceBase { // Services - batteryService: Service; - humidityservice?: Service; - temperatureservice?: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - CurrentTemperature?: CharacteristicValue; - CurrentRelativeHumidity!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; - - // BLE Status - BLE_Celsius!: temperature['c']; - BLE_Fahrenheit!: temperature['f']; - BLE_BatteryLevel!: serviceData['battery']; - BLE_CurrentTemperature!: serviceData['temperature']; - BLE_CurrentRelativeHumidity!: serviceData['humidity']; - - // BLE Others - BLE_IsConnected?: boolean; - - //MQTT stuff - mqttClient: MqttClient | null = null; - - // EVE history service handler - historyService?: any; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private HumiditySensor?: { + Service: Service; + CurrentRelativeHumidity: CharacteristicValue; + }; + + private TemperatureSensor?: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; // Updates ioSensorUpdateInProgress!: boolean; doIOSensorUpdate: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.setupHistoryService(device); - this.setupMqtt(device); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doIOSensorUpdate = new Subject(); this.ioSensorUpdateInProgress = false; @@ -96,30 +48,42 @@ export class IOSensor { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'WoIOSensor') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Temperature Sensor property + if (!device.iosensor?.hide_temperature) { + this.TemperatureSensor = { + Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + CurrentTemperature: accessory.context.CurrentTemperature || 30, + }; + } + + // Initialize Humidity Sensor property + if (!device.iosensor?.hide_humidity) { + this.HumiditySensor = { + Service: this.accessory.addService(this.hap.Service.HumiditySensor), + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, + }; + } + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; // Temperature Sensor Service - if (device.meter?.hide_temperature) { + if (device.iosensor?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureservice!); - } else if (!this.temperatureservice) { + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } else if (!this.TemperatureSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureservice = `${accessory.displayName} Temperature Sensor`; - (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice; + const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; + (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureservice + this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); + this.TemperatureSensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -129,49 +93,43 @@ export class IOSensor { minStep: 0.1, }) .onGet(() => { - return this.CurrentTemperature!; + return this.TemperatureSensor!.CurrentTemperature!; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); } // Humidity Sensor Service - if (device.meter?.hide_humidity) { + if (device.iosensor?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor); - accessory.removeService(this.humidityservice!); - } else if (!this.humidityservice) { + this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor!.Service); + } else if (!this.HumiditySensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const humidityservice = `${accessory.displayName} Humidity Sensor`; - (this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityservice; + const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; + (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) + || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; - this.humidityservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); - if (!this.humidityservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidityservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); - } - this.humidityservice + this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + this.HumiditySensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, }) .onGet(() => { - return this.CurrentRelativeHumidity; + return this.HumiditySensor!.CurrentRelativeHumidity; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -191,13 +149,18 @@ export class IOSensor { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); if (context.scale === 'CELSIUS') { const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity) = ' + `Webhook:(${temperature}, ${humidity}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; + if (this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (this.device.iosensor?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } this.updateHomeKitCharacteristics(); } } catch (e: any) { @@ -208,89 +171,61 @@ export class IOSensor { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Humidity - if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + if (!this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = serviceData.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // Current Temperature - if (!this.device.meter?.hide_temperature) { - this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius; - this.CurrentTemperature = this.BLE_Celsius; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + if (!this.device.iosensor?.hide_temperature) { + serviceData.temperature!.c < 0 ? 0 : serviceData.temperature!.c > 100 ? 100 : serviceData.temperature!.c; + this.TemperatureSensor!.CurrentTemperature = serviceData.temperature!.c; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + + // BatteryLevel + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Current Relative Humidity - if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + if (!this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = deviceStatus.body.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // Current Temperature - if (!this.device.meter?.hide_temperature) { - this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); - } - - // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; - } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + if (!this.device.iosensor?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); - // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -325,96 +260,35 @@ export class IOSensor { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'w', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'w') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'w', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - if (ad.serviceData.humidity! > 0) { - // reject unreliable data - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity; - } - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + - `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + - `battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -422,11 +296,7 @@ export class IOSensor { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!; - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -447,63 +317,73 @@ export class IOSensor { async updateHomeKitCharacteristics(): Promise { const mqttmessage: string[] = []; const entry = { time: Math.round(new Date().valueOf() / 1000) }; - if (!this.device.meter?.hide_humidity) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + + // CurrentRelativeHumidity + if (!this.device.iosensor?.hide_humidity) { + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, - ); + this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumiditySensor!.CurrentRelativeHumidity); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); if (this.device.mqttURL) { - mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); + mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`); } if (this.device.history) { - entry['humidity'] = this.CurrentRelativeHumidity; + entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity; } } } - if (!this.device.meter?.hide_temperature) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + + // CurrentTemperature + if (!this.device.iosensor?.hide_temperature) { + if (this.TemperatureSensor!.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } else { + this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); if (this.device.mqttURL) { - mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); + mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`); } if (this.device.history) { - entry['temp'] = this.CurrentTemperature; + entry['temp'] = this.TemperatureSensor!.CurrentTemperature; } - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); } } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); if (this.device.mqttURL) { - mqttmessage.push(`"battery": ${this.BatteryLevel}`); + mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); if (this.device.mqttURL) { - mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`); + mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } + + // MQTT Publish if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.CurrentRelativeHumidity) > 0) { + + // History Service + if (!this.device.iosensor?.hide_humidity && (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0)) { // reject unreliable data if (this.device.history) { this.historyService?.addEntry(entry); @@ -511,84 +391,6 @@ export class IOSensor { } } - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); - } - } - } - - /* - * Setup EVE history graph feature if enabled. - */ - async setupHistoryService(device: device & devicesConfig): Promise { - const mac = this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); - this.historyService = device.history - ? new this.platform.fakegatoAPI('room', this.accessory, { - log: this.platform.log, - storage: 'fs', - filename: `${hostname().split('.')[0]}_${mac}_persist.json`, - }) - : null; - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'i', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLERefreshConnection(switchbot: any): Promise { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + ` ${JSON.stringify(switchbot)}`); @@ -598,272 +400,26 @@ export class IOSensor { } } - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); - } - } - - async apiError(e: any): Promise { - if (!this.device.meter?.hide_humidity) { - this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); - } - if (!this.device.meter?.hide_temperature) { - this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - - async deviceContext() { - 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; - } - if (this.BatteryLevel === undefined) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; - } - if (this.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.meter) { - config = device.meter; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); + if (!this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); + if (!this.device.iosensor?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } } - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } + async apiError(e: any): Promise { + if (!this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + if (!this.device.iosensor?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); + } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 9faad360..55270ba7 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -1,93 +1,43 @@ import { request } from 'undici'; -import { hs2rgb, rgb2hs, m2hs, sleep } from '../utils.js'; -import { interval, Subject } from 'rxjs'; +import { interval, skipWhile, Subject } from 'rxjs'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { - Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP, -} from 'homebridge'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; +import { debounceTime, take, tap } from 'rxjs/operators'; +import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class StripLight { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class StripLight extends deviceBase { // Services - lightBulbService!: Service; - - // Characteristic Values - On!: CharacteristicValue; - Hue!: CharacteristicValue; - Saturation!: CharacteristicValue; - Brightness!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - ColorTemperature?: CharacteristicValue; - - // OpenAPI Status - OpenAPI_On: deviceStatus['power']; - OpenAPI_RGB: deviceStatus['color']; - OpenAPI_Brightness: deviceStatus['brightness']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - - // BLE Status - BLE_On: serviceData['state']; - BLE_Brightness: serviceData['brightness']; - - // BLE Others - BLE_IsConnected?: boolean; - - // Config - set_minStep?: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - adaptiveLightingShift?: number; - maxRetries!: number; - delayBetweenRetries!: number; + private LightBulb: { + Service: Service; + On: CharacteristicValue; + Hue: CharacteristicValue; + Saturation: CharacteristicValue; + Brightness: CharacteristicValue; + ColorTemperature?: CharacteristicValue; + }; // Adaptive Lighting AdaptiveLightingController?: ControllerConstructor | Controller; - minKelvin!: number; - maxKelvin!: number; - - // Others - cacheKelvin!: number; - change!: string; + adaptiveLightingShift?: number; // Updates stripLightUpdateInProgress!: boolean; doStripLightUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); + this.adaptiveLighting(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doStripLightUpdate = new Subject(); this.stripLightUpdateInProgress = false; @@ -95,36 +45,35 @@ export class StripLight { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W1701100') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize the LightBulb property + this.LightBulb = { + Service: this.accessory.addService(this.hap.Service.Lightbulb), + On: accessory.context.On || false, + Hue: accessory.context.Hue || 0, + Saturation: accessory.context.Saturation || 0, + Brightness: accessory.context.Brightness || 0, + ColorTemperature: accessory.context.ColorTemperature || 140, + }; // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory - const lightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService; + const LightBulbService = `${accessory.displayName} ${device.deviceType}`; + (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) + || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { - this.accessory.removeService(this.lightBulbService); - this.lightBulbService = this.accessory.addService(this.hap.Service.Lightbulb); + this.accessory.removeService(this.LightBulb.Service); + this.LightBulb.Service = this.accessory.addService(this.hap.Service.Lightbulb); this.accessory.context.adaptiveLighting = false; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); } - this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the On characteristic - this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); // handle Brightness events using the Brightness characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ minStep: this.minStep(device), @@ -133,12 +82,12 @@ export class StripLight { validValueRanges: [0, 100], }) .onGet(() => { - return this.Brightness; + return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); // handle ColorTemperature events using the ColorTemperature characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ minValue: 140, @@ -146,12 +95,12 @@ export class StripLight { validValueRanges: [140, 500], }) .onGet(() => { - return this.ColorTemperature!; + return this.LightBulb.ColorTemperature!; }) .onSet(this.ColorTemperatureSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ minValue: 0, @@ -159,12 +108,12 @@ export class StripLight { validValueRanges: [0, 360], }) .onGet(() => { - return this.Hue; + return this.LightBulb.Hue; }) .onSet(this.HueSet.bind(this)); // handle Hue events using the Hue characteristic - this.lightBulbService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ minValue: 0, @@ -172,13 +121,13 @@ export class StripLight { validValueRanges: [0, 100], }) .onGet(() => { - return this.Saturation; + return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.lightBulbService, { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { customTemperatureAdjustment: this.adaptiveLightingShift, }); this.accessory.configureController(this.AdaptiveLightingController); @@ -206,13 +155,13 @@ export class StripLight { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { powerState, brightness, color, colorTemperature } = context; - const { On, Brightness, Hue, Saturation, ColorTemperature } = this; + const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(powerState, brightness, color, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); - this.On = powerState === 'ON' ? true : false; - this.Brightness = brightness; + this.LightBulb.On = powerState === 'ON' ? true : false; + this.LightBulb.Brightness = brightness; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); const [red, green, blue] = color!.split(':'); @@ -226,12 +175,12 @@ export class StripLight { ); // Hue - this.Hue = hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); // Saturation - this.Saturation = saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -264,56 +213,71 @@ export class StripLight { }); } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // State - switch (this.BLE_On) { + switch (serviceData.state) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + + // Brightness + this.LightBulb.Brightness = Number(serviceData.brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + + // Color, Hue & Brightness + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${serviceData.red}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${serviceData.green}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${serviceData.blue}`); + + const [hue, saturation] = rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)); + this.debugLog( + `${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`, + ); + + // Hue + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); + + // Saturation + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + + // ColorTemperature + if (serviceData.color_temperature) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${serviceData.color_temperature}`); + this.LightBulb.ColorTemperature = serviceData.color_temperature!; + + this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_On) { + switch (deviceStatus.body.power) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); // Brightness - this.Brightness = Number(this.OpenAPI_Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + this.LightBulb.Brightness = Number(deviceStatus.body.brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); // Color, Hue & Brightness - if (this.OpenAPI_RGB) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(this.OpenAPI_RGB)}`); - const [red, green, blue] = this.OpenAPI_RGB!.split(':'); + if (deviceStatus.body.color) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(deviceStatus.body.color)}`); + const [red, green, blue] = deviceStatus.body.color!.split(':'); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); @@ -324,17 +288,16 @@ export class StripLight { ); // Hue - this.Hue = hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + this.LightBulb.Hue = hue; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); // Saturation - this.Saturation = saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + this.LightBulb.Saturation = saturation; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -369,85 +332,35 @@ export class StripLight { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'r', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'r') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'r', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + - `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` + - `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -455,11 +368,7 @@ export class StripLight { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.OpenAPI_RGB = deviceStatus.body.color; - this.OpenAPI_Brightness = deviceStatus.body.brightness; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -508,8 +417,9 @@ export class StripLight { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + if (this.LightBulb.On !== this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` + + ` On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -523,11 +433,11 @@ export class StripLight { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (this.LightBulb.On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -538,35 +448,39 @@ export class StripLight { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.On = false; + + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); // Push Brightness Update - if (this.On) { + if (this.LightBulb.On) { await this.BLEpushBrightnessChanges(); } // Push Hue & Saturation Update - if (this.On) { + if (this.LightBulb.On) { await this.BLEpushRGBChanges(); } + if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { + const kelvin = Math.round(1000000 / Number(this.LightBulb.ColorTemperature)); + this.accessory.context.kelvin = kelvin; + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); + } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` + + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); } } async BLEpushBrightnessChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushBrightnessChanges`); - if (this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -580,12 +494,12 @@ export class StripLight { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.Brightness}`); - return await device_list[0].setBrightness(this.Brightness); + this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.LightBulb.Brightness}`); + return await device_list[0].setBrightness(this.LightBulb.Brightness); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.On = false; + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -598,7 +512,7 @@ export class StripLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + - `Brightness: ${this.Brightness}, ` + + `Brightness: ${this.LightBulb.Brightness}, ` + `BrightnessCached: ${this.accessory.context.Brightness}`, ); } @@ -606,11 +520,11 @@ export class StripLight { async BLEpushRGBChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushRGBChanges`); - if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`); + if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`); - const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); const switchbot = await this.platform.connectBLE(); @@ -626,12 +540,12 @@ export class StripLight { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.Brightness, red, green, blue)}`); - return await device_list[0].setRGB(this.Brightness, red, green, blue); + this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.LightBulb.Brightness, red, green, blue)}`); + return await device_list[0].setRGB(this.LightBulb.Brightness, red, green, blue); }) .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); - this.On = false; + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -643,17 +557,17 @@ export class StripLight { }); } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, + `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, ` + + `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, ); } } async openAPIpushChanges() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.On !== this.accessory.context.On) { - const command = this.On ? 'turnOn' : 'turnOff'; - /*if (this.On) { + if (this.LightBulb.On !== this.accessory.context.On) { + const command = this.LightBulb.On ? 'turnOn' : 'turnOff'; + /*if (this.LightBulb.On) { command = 'turnOn'; } else { command = 'turnOff'; @@ -679,7 +593,7 @@ export class StripLight { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -694,26 +608,26 @@ export class StripLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } // Push Hue & Saturation Update - if (this.On) { + if (this.LightBulb.On) { await this.pushHueSaturationChanges(); } // Push Brightness Update - if (this.On) { + if (this.LightBulb.On) { await this.pushBrightnessChanges(); } } async pushHueSaturationChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushHueSaturationChanges`); - if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`); - const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`); + const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); // Make Push On request to the API const bodyChange = JSON.stringify({ @@ -737,7 +651,7 @@ export class StripLight { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -751,18 +665,18 @@ export class StripLight { } } else { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, + `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + + `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, ); } } async pushBrightnessChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushBrightnessChanges`); - if (this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { const bodyChange = JSON.stringify({ command: 'setBrightness', - parameter: `${this.Brightness}`, + parameter: `${this.LightBulb.Brightness}`, commandType: 'command', }); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); @@ -781,7 +695,7 @@ export class StripLight { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -796,7 +710,7 @@ export class StripLight { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges,` + - `Brightness: ${this.Brightness}, ` + + `Brightness: ${this.LightBulb.Brightness}, ` + `BrightnessCached: ${this.accessory.context.Brightness}`, ); } @@ -806,13 +720,13 @@ export class StripLight { * Handle requests to set the value of the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - if (this.On === this.accessory.context.On) { + if (this.LightBulb.On === this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); } - this.On = value; + this.LightBulb.On = value; this.doStripLightUpdate.next(); } @@ -820,15 +734,15 @@ export class StripLight { * Handle requests to set the value of the "Brightness" characteristic */ async BrightnessSet(value: CharacteristicValue): Promise { - if (this.Brightness === this.accessory.context.Brightness) { + if (this.LightBulb.Brightness === this.accessory.context.Brightness) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } - this.Brightness = value; + this.LightBulb.Brightness = value; this.doStripLightUpdate.next(); } @@ -836,30 +750,31 @@ export class StripLight { * Handle requests to set the value of the "ColorTemperature" characteristic */ async ColorTemperatureSet(value: CharacteristicValue): Promise { - if (this.ColorTemperature === this.accessory.context.ColorTemperature) { + if (this.LightBulb.ColorTemperature === this.accessory.context.ColorTemperature) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set ColorTemperature: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`); } + const minKelvin = 2000; + const maxKelvin = 9000; // Convert mired to kelvin to nearest 100 (SwitchBot seems to need this) const kelvin = Math.round(1000000 / Number(value) / 100) * 100; - // Check and increase/decrease kelvin to range of device - const k = Math.min(Math.max(kelvin, this.minKelvin), this.maxKelvin); + const k = Math.min(Math.max(kelvin, minKelvin), maxKelvin); - if (!this.accessory.context.On || this.cacheKelvin === k) { + if (!this.accessory.context.On || this.accessory.context.maxKelvin === k) { return; } // Updating the hue/sat to the corresponding values mimics native adaptive lighting const hs = m2hs(value); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); - this.ColorTemperature = value; + this.LightBulb.ColorTemperature = value; this.doStripLightUpdate.next(); } @@ -867,17 +782,17 @@ export class StripLight { * Handle requests to set the value of the "Hue" characteristic */ async HueSet(value: CharacteristicValue): Promise { - if (this.Hue === this.accessory.context.Hue) { + if (this.LightBulb.Hue === this.accessory.context.Hue) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Hue: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Hue = value; + this.LightBulb.Hue = value; this.doStripLightUpdate.next(); } @@ -885,89 +800,61 @@ export class StripLight { * Handle requests to set the value of the "Saturation" characteristic */ async SaturationSet(value: CharacteristicValue): Promise { - if (this.Saturation === this.accessory.context.Saturation) { + if (this.LightBulb.Saturation === this.accessory.context.Saturation) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Saturation: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); - this.Saturation = value; + this.LightBulb.Saturation = value; this.doStripLightUpdate.next(); } async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.LightBulb.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } else { - this.accessory.context.On = this.On; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.LightBulb.On; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`); } // Brightness - if (this.Brightness === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + if (this.LightBulb.Brightness === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); } else { - this.accessory.context.Brightness = this.Brightness; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); + this.accessory.context.Brightness = this.LightBulb.Brightness; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`); } // ColorTemperature - if (this.ColorTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + if (this.LightBulb.ColorTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } else { - this.accessory.context.ColorTemperature = this.ColorTemperature; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.ColorTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`); + this.accessory.context.ColorTemperature = this.LightBulb.ColorTemperature; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.LightBulb.ColorTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ColorTemperature: ${this.LightBulb.ColorTemperature}`); } // Hue - if (this.Hue === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`); + if (this.LightBulb.Hue === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); } else { - this.accessory.context.Hue = this.Hue; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, this.Hue); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`); + this.accessory.context.Hue = this.LightBulb.Hue; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, this.LightBulb.Hue); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.LightBulb.Hue}`); } // Saturation - if (this.Saturation === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + if (this.LightBulb.Saturation === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } else { - this.accessory.context.Saturation = this.Saturation; - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, this.Saturation); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`); - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`); - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'r', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.Saturation = this.LightBulb.Saturation; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, this.LightBulb.Saturation); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.LightBulb.Saturation}`); } } @@ -987,33 +874,14 @@ export class StripLight { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - minStep(device: device & devicesConfig): number { + let set_minStep: number; if (device.striplight?.set_minStep) { - this.set_minStep = device.striplight?.set_minStep; + set_minStep = device.striplight?.set_minStep; } else { - this.set_minStep = 1; + set_minStep = 1; } - return this.set_minStep; + return set_minStep; } async adaptiveLighting(device: device & devicesConfig): Promise { @@ -1026,277 +894,17 @@ export class StripLight { } } - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); - } - } - - apiError(e: any): void { - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, e); - this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, e); - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.Hue === undefined) { - this.Hue = 0; - } else { - this.Hue = this.accessory.context.Hue; - } - if (this.Brightness === undefined) { - this.Brightness = 0; - } else { - this.Brightness = this.accessory.context.Brightness; - } - if (this.Saturation === undefined) { - this.Saturation = 0; - } else { - this.Saturation = this.accessory.context.Saturation; - } - if (this.ColorTemperature === undefined) { - this.ColorTemperature = 140; - } else { - this.ColorTemperature = this.accessory.context.ColorTemperature; - } - this.minKelvin = 2000; - this.maxKelvin = 9000; - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.striplight) { - config = device.striplight; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + async apiError(e: any): Promise { + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, e); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e); } } diff --git a/src/device/lock.ts b/src/device/lock.ts index 02dd84ce..601c3af1 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -1,80 +1,45 @@ import { request } from 'undici'; -import { sleep } from '../utils.js'; +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, deviceStatus, Devices, serviceData, SwitchBotPlatformConfig } from '../settings.js'; - -export class Lock { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, deviceStatus, Devices, serviceData } from '../settings.js'; + +export class Lock extends deviceBase { // Services - lockService: Service; - batteryService: Service; - contactSensorService?: Service; - latchButtonService?: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - LockTargetState!: CharacteristicValue; - LockCurrentState!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - ContactSensorState!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_LockCurrentState!: deviceStatus['lockState']; - OpenAPI_ContactSensorState!: deviceStatus['doorState']; - - // BLE Status - BLE_BatteryLevel: serviceData['battery']; - BLE_LockCurrentState: serviceData['state']; - BLE_Calibration: serviceData['calibration']; - BLE_ContactSensorState: serviceData['door_open']; - - // BLE Others - BLE_IsConnected?: boolean; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private LockMechanism: { + Service: Service; + LockTargetState: CharacteristicValue; + LockCurrentState: CharacteristicValue; + }; + + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private ContactSensor?: { + Service: Service; + ContactSensorState: CharacteristicValue; + }; + + private Switch?: { + Service: Service; + On: CharacteristicValue; + }; // Updates lockUpdateInProgress!: boolean; doLockUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doLockUpdate = new Subject(); this.lockUpdateInProgress = false; @@ -82,96 +47,88 @@ export class Lock { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W1601700') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); - - // get the LockMechanism service if it exists, otherwise create a new LockMechanism service - // you can create multiple services for each accessory - const lockService = `${accessory.displayName} ${device.deviceType}`; - (this.lockService = accessory.getService(this.hap.Service.LockMechanism) - || accessory.addService(this.hap.Service.LockMechanism)), lockService; - - this.lockService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lockService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lockService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); + // Initialize LockMechanism property + this.LockMechanism = { + Service: accessory.addService(this.hap.Service.LockMechanism), + LockTargetState: accessory.context.LockTargetState || this.hap.Characteristic.LockTargetState.SECURED, + LockCurrentState: accessory.context.LockCurrentState || this.hap.Characteristic.LockCurrentState.SECURED, + }; + + // Initialize Battery property + this.Battery = { + Service: accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; + + // Initialize ContactSensor property + if (!this.device.lock?.hide_contactsensor) { + this.ContactSensor = { + Service: accessory.addService(this.hap.Service.ContactSensor), + ContactSensorState: accessory.context.ContactSensorState || this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, + }; } - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/LockMechanism + // Initialize Latch Button Service + if (device.lock?.activate_latchbutton) { + this.Switch = { + Service: accessory.addService(this.hap.Service.Switch), + On: accessory.context.On || false, + }; + } + // get the LockMechanism service if it exists, otherwise create a new LockMechanism service + // you can create multiple services for each accessory + const LockMechanismService = `${accessory.displayName} ${device.deviceType}`; + (this.LockMechanism.Service = accessory.getService(this.hap.Service.LockMechanism) + || accessory.addService(this.hap.Service.LockMechanism)), LockMechanismService; - // create handlers for required characteristics - this.lockService.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.LockTargetStateSet.bind(this)); + this.LockMechanism.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.LockMechanism.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.LockTargetStateSet.bind(this)); // Latch Button Service if (device.lock?.activate_latchbutton === false) { // remove the service when this variable is false this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Latch Button Service`); - this.latchButtonService = accessory.getService(this.hap.Service.Switch); - if (this.latchButtonService) { - accessory.removeService(this.latchButtonService); - this.latchButtonService = undefined; // Reset the service variable to undefined - } - } else - if (!this.latchButtonService) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Adding Latch Button Service`); - const latchServiceName = `${accessory.displayName} Latch`; - this.latchButtonService = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch, latchServiceName, 'LatchButtonServiceIdentifier'); - - this.latchButtonService.setCharacteristic(this.hap.Characteristic.Name, latchServiceName); + this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service; + accessory.removeService(this.Switch!.Service); + } else if (!this.Switch!.Service && device.lock?.activate_latchbutton === true) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Adding Latch Button Service`); + const latchServiceName = `${accessory.displayName} Latch`; + this.Switch!.Service = accessory.getService(this.hap.Service.Switch) + || accessory.addService(this.hap.Service.Switch, latchServiceName, 'Switch.ServiceIdentifier'); - if (!this.latchButtonService.testCharacteristic(this.hap.Characteristic.On)) { - this.latchButtonService.addCharacteristic(this.hap.Characteristic.On); - } + this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, latchServiceName); - this.latchButtonService.getCharacteristic(this.hap.Characteristic.On) - .on('set', (value, callback) => { - if (typeof value === 'boolean') { - this.handleLatchCharacteristic(value, callback); - } else { - callback(new Error('Wrong characteristic value type')); - } - }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Latch Button Service already exists`); - } + this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + } else { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Latch Button Service Not Added`); + } // Contact Sensor Service if (device.lock?.hide_contactsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); - this.contactSensorService = this.accessory.getService(this.hap.Service.ContactSensor); - accessory.removeService(this.contactSensorService!); - } else if (!this.contactSensorService) { + this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; + accessory.removeService(this.ContactSensor!.Service); + } else if (!this.ContactSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Contact Sensor Service`); - const contactSensorService = `${accessory.displayName} Contact Sensor`; - (this.contactSensorService = this.accessory.getService(this.hap.Service.ContactSensor) - || this.accessory.addService(this.hap.Service.ContactSensor)), contactSensorService; + const ContactSensorService = `${accessory.displayName} Contact Sensor`; + (this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) + || this.accessory.addService(this.hap.Service.ContactSensor)), ContactSensorService; - this.contactSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Contact Sensor`); - if (!this.contactSensorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.contactSensorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Contact Sensor`); - } + this.ContactSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, ContactSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Contact Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Update Homekit this.updateHomeKitCharacteristics(); @@ -190,12 +147,12 @@ export class Lock { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { lockState } = context; - const { LockCurrentState } = this; + const { LockCurrentState } = this.LockMechanism; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(lockState) = ' + `Webhook:(${lockState}), ` + `current:(${LockCurrentState})`); - this.LockCurrentState = lockState === 'LOCKED' ? 1 : 0; + this.LockMechanism.LockCurrentState = lockState === 'LOCKED' ? 1 : 0; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -227,134 +184,79 @@ export class Lock { }); } - /** - * Method for handling the LatchCharacteristic - */ - async handleLatchCharacteristic(value: boolean, callback) { - this.debugLog(`handleLatchCharacteristic called with value: ${value}`); - - if (value) { - this.debugLog('Attempting to open the latch'); - - this.openAPIpushChanges(value).then(() => { - this.debugLog('Latch opened successfully'); - this.debugLog(`LatchButtonService is: ${this.latchButtonService ? 'available' : 'not available'}`); - - // simulate button press to turn the switch back off - if (this.latchButtonService) { - const latchButtonService = this.latchButtonService; - // Simulate a button press by waiting a short period before turning the switch off - setTimeout(() => { - latchButtonService.getCharacteristic(this.hap.Characteristic.On).updateValue(false); - this.debugLog('Latch button switched off automatically.'); - }, 500); // 500 ms delay - } - callback(null); - }).catch((error) => { - // Log the error if the operation failed - this.debugLog(`Error opening latch: ${error}`); - - // Ensure we turn the switch back off even in case of an error - if (this.latchButtonService) { - this.latchButtonService.getCharacteristic(this.hap.Characteristic.On).updateValue(false); - this.debugLog('Latch button switched off after an error.'); - } - - callback(error); - }); - - } else { - this.debugLog('Switch is off, nothing to do'); - callback(null); - } - } - - - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - /* } else if (this.BLE) { - await this.BLEparseStatus();*/ - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { + // BLE Status this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); - switch (this.BLE_LockCurrentState) { + switch (serviceData.status) { case 'locked': - this.LockCurrentState = this.hap.Characteristic.LockCurrentState.SECURED; - this.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; + this.LockMechanism.LockCurrentState = this.hap.Characteristic.LockCurrentState.SECURED; + this.LockMechanism.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; break; default: - this.LockCurrentState = this.hap.Characteristic.LockCurrentState.UNSECURED; - this.LockTargetState = this.hap.Characteristic.LockTargetState.UNSECURED; + this.LockMechanism.LockCurrentState = this.hap.Characteristic.LockCurrentState.UNSECURED; + this.LockMechanism.LockTargetState = this.hap.Characteristic.LockTargetState.UNSECURED; } - switch (this.BLE_ContactSensorState) { - case 'opened': - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; - break; - default: - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` LockTargetState: ${this.LockMechanism.LockTargetState}, LockCurrentState: ${this.LockMechanism.LockCurrentState}`); + + // Contact Sensor + if (!this.device.lock?.hide_contactsensor) { + switch (serviceData.door_open) { + case 'opened': + this.ContactSensor!.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; + break; + default: + this.ContactSensor!.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor!.ContactSensorState}`); + } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockTargetState}`); // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_LockCurrentState) { + switch (deviceStatus.body.lockState) { case 'locked': - this.LockCurrentState = this.hap.Characteristic.LockCurrentState.SECURED; - this.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; + this.LockMechanism.LockCurrentState = this.hap.Characteristic.LockCurrentState.SECURED; + this.LockMechanism.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; break; default: - this.LockCurrentState = this.hap.Characteristic.LockCurrentState.UNSECURED; - this.LockTargetState = this.hap.Characteristic.LockTargetState.UNSECURED; + this.LockMechanism.LockCurrentState = this.hap.Characteristic.LockCurrentState.UNSECURED; + this.LockMechanism.LockTargetState = this.hap.Characteristic.LockTargetState.UNSECURED; } - switch (this.OpenAPI_ContactSensorState) { + switch (deviceStatus.body.doorState) { case 'opened': - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; + this.ContactSensor!.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; break; default: - this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; + this.ContactSensor!.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockTargetState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockMechanism.LockTargetState}`); // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -389,92 +291,35 @@ export class Lock { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'o', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'o') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_LockCurrentState = ad.serviceData.status; - this.BLE_ContactSensorState = ad.serviceData.door_open; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'o', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_Calibration = ad.serviceData.calibration; - this.BLE_LockCurrentState = ad.serviceData.status; - this.BLE_ContactSensorState = ad.serviceData.door_open; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} battery: ${ad.serviceData.battery}, ` + - `calibration: ${ad.serviceData.calibration}, status: ${ad.serviceData.status}, battery: ${ad.serviceData.battery}, ` + - `door_open: ${ad.serviceData.door_open}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -482,11 +327,7 @@ export class Lock { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_LockCurrentState = deviceStatus.body.lockState; - this.OpenAPI_ContactSensorState = deviceStatus.body.doorState; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -531,9 +372,9 @@ export class Lock { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.LockTargetState !== this.accessory.context.LockTargetState) { + if (this.LockMechanism.LockTargetState !== this.accessory.context.LockTargetState) { this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges LockTargetState: ${this.LockTargetState}` + + `${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges LockTargetState: ${this.LockMechanism.LockTargetState}` + ` LockTargetStateCached: ${this.accessory.context.LockTargetState}`, ); const switchbot = await this.platform.connectBLE(); @@ -551,8 +392,8 @@ export class Lock { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `LockTargetState: ${this.LockTargetState} sent over BLE, sent successfully`); - this.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; + + `LockTargetState: ${this.LockMechanism.LockTargetState} sent over BLE, sent successfully`); + this.LockMechanism.LockTargetState = this.hap.Characteristic.LockTargetState.SECURED; }) .catch(async (e: any) => { this.apiError(e); @@ -565,7 +406,7 @@ export class Lock { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + - `LockTargetState: ${this.LockTargetState}, ` + + `LockTargetState: ${this.LockMechanism.LockTargetState}, ` + `LockTargetStateCached: ${this.accessory.context.LockTargetState}`, ); } @@ -574,13 +415,13 @@ export class Lock { async openAPIpushChanges(LatchUnlock?): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if ((this.LockTargetState !== this.accessory.context.LockTargetState) || LatchUnlock) { + if ((this.LockMechanism.LockTargetState !== this.accessory.context.LockTargetState) || LatchUnlock) { // Determine the command based on the LockTargetState or the forceUnlock parameter let command = ''; if (LatchUnlock) { command = 'unlock'; } else { - command = this.LockTargetState ? 'lock' : 'unlock'; + command = this.LockMechanism.LockTargetState ? 'lock' : 'unlock'; } const bodyChange = JSON.stringify({ command: `${command}`, @@ -603,7 +444,7 @@ export class Lock { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -618,7 +459,7 @@ export class Lock { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `LockTargetState: ${this.LockTargetState}, ` + + `LockTargetState: ${this.LockMechanism.LockTargetState}, ` + `LockTargetStateCached: ${this.accessory.context.LockTargetState}`, ); } @@ -628,81 +469,97 @@ export class Lock { * Handle requests to set the value of the "On" characteristic */ async LockTargetStateSet(value: CharacteristicValue): Promise { - if (this.LockTargetState === this.accessory.context.LockTargetState) { + if (this.LockMechanism.LockTargetState === this.accessory.context.LockTargetState) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set LockTargetState: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); } - this.LockTargetState = value; + this.LockMechanism.LockTargetState = value; + this.doLockUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + async OnSet(value: CharacteristicValue): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Latch Button Set On: ${value}`); + if (value) { + this.debugLog('Attempting to open the latch'); + + this.openAPIpushChanges(value).then(() => { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Latch opened successfully`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` SwitchService is: ${this.Switch!.Service ? 'available' : 'not available'}`); + + // simulate button press to turn the switch back off + if (this.Switch!.Service) { + const SwitchService = this.Switch!.Service; + // Simulate a button press by waiting a short period before turning the switch off + setTimeout(() => { + SwitchService.getCharacteristic(this.hap.Characteristic.On).updateValue(false); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Latch button switched off automatically.`); + }, 500); // 500 ms delay + } + }).catch((e: any) => { + // Log the error if the operation failed + this.debugLog(`Error opening latch: ${e}`); + // Ensure we turn the switch back off even in case of an error + if (this.Switch!.Service) { + this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(false); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Latch button switched off after an error.`); + } + }); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch is off, nothing to do`); + } + + this.Switch!.On = value; this.doLockUpdate.next(); } async updateHomeKitCharacteristics(): Promise { if (!this.device.lock?.hide_contactsensor) { - if (this.ContactSensorState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + if (this.ContactSensor!.ContactSensorState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor!.ContactSensorState}`); } else { - this.accessory.context.ContactSensorState = this.ContactSensorState; - this.contactSensorService?.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensorState); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`); + this.accessory.context.ContactSensorState = this.ContactSensor!.ContactSensorState; + this.ContactSensor!.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensor!.ContactSensorState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ContactSensorState: ${this.ContactSensor!.ContactSensorState}`); } } - if (this.LockTargetState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockTargetState: ${this.LockTargetState}`); + if (this.LockMechanism.LockTargetState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockTargetState: ${this.LockMechanism.LockTargetState}`); } else { - this.accessory.context.LockTargetState = this.LockTargetState; - this.lockService.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.LockTargetState); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockTargetState}`); + this.accessory.context.LockTargetState = this.LockMechanism.LockTargetState; + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.LockMechanism.LockTargetState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` LockTargetState: ${this.LockMechanism.LockTargetState}`); } - if (this.LockCurrentState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockCurrentState: ${this.LockCurrentState}`); + if (this.LockMechanism.LockCurrentState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockCurrentState: ${this.LockMechanism.LockCurrentState}`); } else { - this.accessory.context.LockCurrentState = this.LockCurrentState; - this.lockService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.LockCurrentState); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockCurrentState: ${this.LockCurrentState}`); + this.accessory.context.LockCurrentState = this.LockMechanism.LockCurrentState; + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.LockMechanism.LockCurrentState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` LockCurrentState: ${this.LockMechanism.LockCurrentState}`); } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` BatteryLevel: ${this.Battery.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'c', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } @@ -722,250 +579,22 @@ export class Lock { } } - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + if (!this.device.lock?.hide_contactsensor) { + this.ContactSensor!.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, + this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED); + } + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); } } async apiError(e: any): Promise { if (!this.device.lock?.hide_contactsensor) { - this.contactSensorService?.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e); - } - this.lockService.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); - this.lockService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); - } - - async deviceContext() { - if (this.LockTargetState === undefined) { - this.LockTargetState = false; - } else { - this.LockTargetState = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.lock) { - config = device.lock; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; + this.ContactSensor!.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e); } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 0c5d4b9a..f65e837c 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -1,114 +1,84 @@ -import asyncmqtt from 'async-mqtt'; -import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, HAP } from 'homebridge'; -import { MqttClient } from 'mqtt'; -import { hostname } from 'os'; -import { interval } from 'rxjs'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * meter.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, device, deviceStatus, devicesConfig, serviceData, temperature, SwitchBotPlatformConfig } from '../settings.js'; -import { sleep } from '../utils.js'; - -export class Meter { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +import { Subject, interval, skipWhile } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +export class Meter extends deviceBase { // Services - batteryService: Service; - humidityService?: Service; - temperatureService?: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - CurrentTemperature?: CharacteristicValue; - CurrentRelativeHumidity?: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; - - // BLE Status - BLE_Celsius!: temperature['c']; - BLE_Fahrenheit!: temperature['f']; - BLE_BatteryLevel!: serviceData['battery']; - BLE_CurrentTemperature!: serviceData['temperature']; - BLE_CurrentRelativeHumidity!: serviceData['humidity']; - - // BLE Others - BLE_IsConnected?: boolean; - - //MQTT stuff - mqttClient: MqttClient | null = null; - - // EVE history service handler - historyService?: any; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; - - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private HumiditySensor?: { + Service: Service; + CurrentRelativeHumidity: CharacteristicValue; + }; + + private TemperatureSensor?: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; + + // Updates + meterUpdateInProgress!: boolean; + doMeterUpdate: Subject; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.setupHistoryService(device); - this.setupMqtt(device); - this.deviceRetry(device); - this.deviceConfig(device); - - this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity; - this.CurrentTemperature = accessory.context.CurrentTemperature; + super(platform, accessory, device); + // this is subject we use to track when we need to POST changes to the SwitchBot API + this.doMeterUpdate = new Subject(); + this.meterUpdateInProgress = false; // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'METERTH-S1') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Temperature Sensor property + if (!device.meter?.hide_temperature) { + this.TemperatureSensor = { + Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + CurrentTemperature: accessory.context.CurrentTemperature || 30, + }; + } + + // Initialize Humidity Sensor property + if (!device.meter?.hide_humidity) { + this.HumiditySensor = { + Service: this.accessory.addService(this.hap.Service.HumiditySensor), + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, + }; + } + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; // Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureService!); - } else if (!this.temperatureService) { + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } else if (!this.TemperatureSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureService = `${accessory.displayName} Temperature Sensor`; - (this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureService; + const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; + (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - this.temperatureService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureService + this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); + this.TemperatureSensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -118,7 +88,7 @@ export class Meter { minStep: 0.1, }) .onGet(() => { - return this.CurrentTemperature!; + return this.TemperatureSensor!.CurrentTemperature!; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); @@ -127,46 +97,41 @@ export class Meter { // Humidity Sensor Service if (device.meter?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor); - accessory.removeService(this.humidityService!); - } else if (!this.humidityService) { + this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor!.Service); + } else if (!this.HumiditySensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const humidityService = `${accessory.displayName} Humidity Sensor`; - (this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityService; + const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; + (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) + || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; - this.humidityService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); - if (!this.humidityService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidityService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); - } - this.humidityService + this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + this.HumiditySensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, }) .onGet(() => { - return this.CurrentRelativeHumidity!; + return this.HumiditySensor!.CurrentRelativeHumidity!; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and update Homekit this.updateHomeKitCharacteristics(); // Start an update interval interval(this.deviceRefreshRate * 1000) + .pipe(skipWhile(() => this.meterUpdateInProgress)) .subscribe(async () => { await this.refreshStatus(); }); @@ -179,13 +144,18 @@ export class Meter { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); if (context.scale === 'CELSIUS') { const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity) = ' + `Webhook:(${temperature}, ${humidity}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; + if (this.device.meter?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (this.device.meter?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } this.updateHomeKitCharacteristics(); } } catch (e: any) { @@ -196,90 +166,62 @@ export class Meter { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // BatteryLevel - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 15) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); // CurrentRelativeHumidity if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + this.HumiditySensor!.CurrentRelativeHumidity = serviceData.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // CurrentTemperature if (!this.device.meter?.hide_temperature) { - this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius; - this.CurrentTemperature = this.BLE_Celsius; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + serviceData.temperature!.c < 0 ? 0 : serviceData.temperature!.c > 100 ? 100 : serviceData.temperature!.c; + this.TemperatureSensor!.CurrentTemperature = serviceData.temperature!.c; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + // Battery + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // CurrentRelativeHumidity if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + this.HumiditySensor!.CurrentRelativeHumidity = deviceStatus.body.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // CurrentTemperature if (!this.device.meter?.hide_temperature) { - this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); - } - - // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; - } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -313,97 +255,36 @@ export class Meter { this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { - await switchbot.startScan({ - model: 'T', - id: this.device.bleMac, - }); + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'T') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity < 0 ? 0 : ad.serviceData.humidity > 100 ? 100 : ad.serviceData.humidity; - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'T', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - if (ad.serviceData.humidity! > 0) { - // reject unreliable data - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity; - } - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + - `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + - `battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -411,11 +292,7 @@ export class Meter { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!; - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -439,64 +316,72 @@ export class Meter { // CurrentRelativeHumidity if (!this.device.meter?.hide_humidity) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { + this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumiditySensor!.CurrentRelativeHumidity); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); if (this.device.mqttURL) { - mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); + mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`); } if (this.device.history) { - entry['humidity'] = this.CurrentRelativeHumidity; + entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity; } - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); } } + // CurrentTemperature if (!this.device.meter?.hide_temperature) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + if (this.TemperatureSensor!.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } else { + this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); if (this.device.mqttURL) { - mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); + mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`); } if (this.device.history) { - entry['temp'] = this.CurrentTemperature; + entry['temp'] = this.TemperatureSensor!.CurrentTemperature; } - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); } } + // BatteryLevel - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); if (this.device.mqttURL) { - mqttmessage.push(`"battery": ${this.BatteryLevel}`); + mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); if (this.device.mqttURL) { - mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`); + mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } - // MQTT + // MQTT Publish if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.CurrentRelativeHumidity) > 0) { + + // History Service + if (!this.device.meter?.hide_humidity && (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0)) { // reject unreliable data if (this.device.history) { this.historyService?.addEntry(entry); @@ -504,84 +389,6 @@ export class Meter { } } - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); - } - } - } - - /* - * Setup EVE history graph feature if enabled. - */ - async setupHistoryService(device: device & devicesConfig): Promise { - const mac = this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); - this.historyService = device.history - ? new this.platform.fakegatoAPI('room', this.accessory, { - log: this.platform.log, - storage: 'fs', - filename: `${hostname().split('.')[0]}_${mac}_persist.json`, - }) - : null; - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'T', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLERefreshConnection(switchbot: any): Promise { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + ` ${JSON.stringify(switchbot)}`); @@ -591,272 +398,26 @@ export class Meter { } } - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + if (!this.device.meter?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); + } + if (!this.device.meter?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); + } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } } async apiError(e: any): Promise { if (!this.device.meter?.hide_humidity) { - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); } if (!this.device.meter?.hide_temperature) { - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - - async deviceContext() { - 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; - } - if (this.BatteryLevel === undefined) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; - } - if (this.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.meter) { - config = device.meter; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 3db09af5..d14aa02c 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -1,116 +1,89 @@ -import asyncmqtt from 'async-mqtt'; -import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, HAP } from 'homebridge'; -import { MqttClient } from 'mqtt'; -import { hostname } from 'os'; -import { interval } from 'rxjs'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * meterplus.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, device, deviceStatus, devicesConfig, serviceData, temperature, SwitchBotPlatformConfig } from '../settings.js'; -import { sleep } from '../utils.js'; +import { Subject, interval, skipWhile } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class MeterPlus { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class MeterPlus extends deviceBase { // Services - batteryService: Service; - humidityService?: Service; - temperatureService?: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - CurrentTemperature?: CharacteristicValue; - CurrentRelativeHumidity?: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_CurrentTemperature: deviceStatus['temperature']; - OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity']; - - // BLE Status - BLE_Celsius!: temperature['c']; - BLE_Fahrenheit!: temperature['f']; - BLE_BatteryLevel!: serviceData['battery']; - BLE_CurrentTemperature!: serviceData['temperature']; - BLE_CurrentRelativeHumidity!: serviceData['humidity']; - - // BLE Others - BLE_IsConnected?: boolean; - - //MQTT stuff - mqttClient: MqttClient | null = null; - - // EVE history service handler - historyService?: any; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; - - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private HumiditySensor?: { + Service: Service; + CurrentRelativeHumidity: CharacteristicValue; + }; + + private TemperatureSensor?: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; + + // Updates + meterUpdateInProgress!: boolean; + doMeterUpdate: Subject; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.setupHistoryService(device); - this.setupMqtt(device); - this.deviceRetry(device); - this.deviceConfig(device); + super(platform, accessory, device); + // this is subject we use to track when we need to POST changes to the SwitchBot API + this.doMeterUpdate = new Subject(); + this.meterUpdateInProgress = false; // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, this.model(device)) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Temperature Sensor property + if (!device.meter?.hide_temperature) { + this.TemperatureSensor = { + Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + CurrentTemperature: accessory.context.CurrentTemperature || 30, + }; + } + + // Initialize Humidity Sensor property + if (!device.meter?.hide_humidity) { + this.HumiditySensor = { + Service: this.accessory.addService(this.hap.Service.HumiditySensor), + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, + }; + } + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; // Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor); - accessory.removeService(this.temperatureService!); - } else if (!this.temperatureService) { + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } else if (!this.TemperatureSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const temperatureService = `${accessory.displayName} Temperature Sensor`; - (this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureService; + const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; + (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) + || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - this.temperatureService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`); - if (!this.temperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.temperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`); - } - this.temperatureService + this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); + this.TemperatureSensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -120,7 +93,7 @@ export class MeterPlus { minStep: 0.1, }) .onGet(() => { - return this.CurrentTemperature!; + return this.TemperatureSensor!.CurrentTemperature!; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); @@ -129,46 +102,41 @@ export class MeterPlus { // Humidity Sensor Service if (device.meter?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor); - accessory.removeService(this.humidityService!); - } else if (!this.humidityService) { + this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor!.Service); + } else if (!this.HumiditySensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const humidityService = `${accessory.displayName} Humidity Sensor`; - (this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityService; + const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; + (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) + || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; - this.humidityService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`); - if (!this.humidityService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.humidityService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`); - } - this.humidityService + this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + this.HumiditySensor!.Service .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, }) .onGet(() => { - return this.CurrentRelativeHumidity!; + return this.HumiditySensor!.CurrentRelativeHumidity!; }); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); // Start an update interval interval(this.deviceRefreshRate * 1000) + .pipe(skipWhile(() => this.meterUpdateInProgress)) .subscribe(async () => { await this.refreshStatus(); }); @@ -181,13 +149,18 @@ export class MeterPlus { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); if (context.scale === 'CELSIUS') { const { temperature, humidity } = context; - const { CurrentTemperature, CurrentRelativeHumidity } = this; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity) = ' + `Webhook:(${temperature}, ${humidity}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.CurrentRelativeHumidity = humidity; - this.CurrentTemperature = temperature; + if (this.device.meter?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (this.device.meter?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } this.updateHomeKitCharacteristics(); } } catch (e: any) { @@ -198,90 +171,61 @@ export class MeterPlus { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 15) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Humidity if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + this.HumiditySensor!.CurrentRelativeHumidity = serviceData.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // Current Temperature if (!this.device.meter?.hide_temperature) { - this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius; - this.CurrentTemperature = this.BLE_Celsius; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); + serviceData.temperature!.c < 0 ? 0 : serviceData.temperature!.c > 100 ? 100 : serviceData.temperature!.c; + this.TemperatureSensor!.CurrentTemperature = serviceData.temperature!.c; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // BatteryLevel - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // CurrentRelativeHumidity if (!this.device.meter?.hide_humidity) { - this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + this.HumiditySensor!.CurrentRelativeHumidity = deviceStatus.body.humidity!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`); } // CurrentTemperature if (!this.device.meter?.hide_temperature) { - this.CurrentTemperature = this.OpenAPI_CurrentTemperature!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); - } - - // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; - } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); - // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -315,97 +259,36 @@ export class MeterPlus { this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { - await switchbot.startScan({ - model: 'i', - id: this.device.bleMac, - }); + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'i') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity < 0 ? 0 : ad.serviceData.humidity > 100 ? 100 : ad.serviceData.humidity; - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: 'i', - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - if (ad.serviceData.humidity! > 0) { - // reject unreliable data - this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity; - } - this.BLE_CurrentTemperature = ad.serviceData.temperature; - this.BLE_Celsius = ad.serviceData.temperature!.c; - this.BLE_Fahrenheit = ad.serviceData.temperature!.f; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + - `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` + - `battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -413,11 +296,7 @@ export class MeterPlus { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!; - this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -441,67 +320,73 @@ export class MeterPlus { // CurrentRelativeHumidity if (!this.device.meter?.hide_humidity) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { + this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumiditySensor!.CurrentRelativeHumidity); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); if (this.device.mqttURL) { - mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`); + mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`); } if (this.device.history) { - entry['humidity'] = this.CurrentRelativeHumidity; + entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity; } - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, - ); } } + // CurrentTemperature if (!this.device.meter?.hide_temperature) { - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + if (this.TemperatureSensor!.CurrentTemperature === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } else { + this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); if (this.device.mqttURL) { - mqttmessage.push(`"temperature": ${this.CurrentTemperature}`); + mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`); } if (this.device.history) { - entry['temp'] = this.CurrentTemperature; + entry['temp'] = this.TemperatureSensor!.CurrentTemperature; } - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); } } + // BatteryLevel - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); if (this.device.mqttURL) { - mqttmessage.push(`"battery": ${this.BatteryLevel}`); + mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } // StatusLowBattery - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); if (this.device.mqttURL) { - mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`); + mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } - // MQTT + // MQTT Publish if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.CurrentRelativeHumidity) > 0) { + + // History Service + if (!this.device.meter?.hide_humidity && (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0)) { // reject unreliable data if (this.device.history) { this.historyService?.addEntry(entry); @@ -509,84 +394,6 @@ export class MeterPlus { } } - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); - } - } - } - - /* - * Setup EVE history graph feature if enabled. - */ - async setupHistoryService(device: device & devicesConfig): Promise { - const mac = this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); - this.historyService = device.history - ? new this.platform.fakegatoAPI('room', this.accessory, { - log: this.platform.log, - storage: 'fs', - filename: `${hostname().split('.')[0]}_${mac}_persist.json`, - }) - : null; - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'i', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLERefreshConnection(switchbot: any): Promise { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + ` ${JSON.stringify(switchbot)}`); @@ -596,282 +403,26 @@ export class MeterPlus { } } - model(device: device & devicesConfig): CharacteristicValue { - let model: string; - if (device.deviceType === 'Meter Plus (JP)') { - model = 'W2201500'; - } else { - model = 'W2301500'; - } - return model; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + if (!this.device.meter?.hide_humidity) { + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); + } + if (!this.device.meter?.hide_temperature) { + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); + } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } } async apiError(e: any): Promise { if (!this.device.meter?.hide_humidity) { - this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); } if (!this.device.meter?.hide_temperature) { - this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - - async deviceContext() { - 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; - } - if (this.BatteryLevel === undefined) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; - } - if (this.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.meter) { - config = device.meter; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/device/motion.ts b/src/device/motion.ts index 06e89bc7..37291916 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -1,84 +1,47 @@ -import { sleep } from '../utils.js'; -import { interval, Subject } from 'rxjs'; -import { skipWhile } from 'rxjs/operators'; + +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * motion.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Subject, interval, skipWhile } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Motion { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Motion extends deviceBase { // Services - batteryService: Service; - motionSensorService!: Service; - lightSensorService?: Service; - - // Characteristic Values - BatteryLevel!: CharacteristicValue; - MotionDetected!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - CurrentAmbientLightLevel!: CharacteristicValue; - - // OpenAPI Others - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_MotionDetected: deviceStatus['moveDetected']; - OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness']; - - // Status - BLE_BatteryLevel!: serviceData['battery']; - BLE_MotionDetected!: serviceData['movement']; - BLE_CurrentAmbientLightLevel!: serviceData['lightLevel']; - - // BLE Others - scanning!: boolean; - BLE_IsConnected?: boolean; - - // Config - set_minLux!: number; - set_maxLux!: number; - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private Battery!: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + + private MotionSensor!: { + Service: Service; + MotionDetected: CharacteristicValue; + }; + + private LightSensor?: { + Service: Service; + CurrentAmbientLightLevel: CharacteristicValue; + }; // Updates motionUbpdateInProgress!: boolean; doMotionUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doMotionUpdate = new Subject(); this.motionUbpdateInProgress = false; @@ -86,54 +49,45 @@ export class Motion { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'W1101500') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + if (this.MotionSensor.MotionDetected === undefined) { + this.MotionSensor.MotionDetected = false; + } else { + this.MotionSensor.MotionDetected = this.accessory.context.MotionDetected; + } // get the Battery service if it exists, otherwise create a new Motion service // you can create multiple services for each accessory - const motionSensorService = `${accessory.displayName} Motion Sensor`; - (this.motionSensorService = accessory.getService(this.hap.Service.MotionSensor) - || accessory.addService(this.hap.Service.MotionSensor)), motionSensorService; + const MotionSensorService = `${accessory.displayName} Motion Sensor`; + (this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) + || accessory.addService(this.hap.Service.MotionSensor)), MotionSensorService; - this.motionSensorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.motionSensorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.motionSensorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.MotionSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/MotionSensor // Light Sensor Service if (device.motion?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor); - accessory.removeService(this.lightSensorService!); - } else if (!this.lightSensorService) { + this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor!.Service); + } else if (!this.LightSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const lightSensorService = `${accessory.displayName} Light Sensor`; - (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService; + const LightSensorService = `${accessory.displayName} Light Sensor`; + (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) + || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`); - this.lightSensorService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`); + this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -152,12 +106,12 @@ export class Motion { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { detectionState } = context; - const { MotionDetected } = this; + const { MotionDetected } = this.MotionSensor; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(detectionState) = ' + `Webhook:(${detectionState}), ` + `current:(${MotionDetected})`); - this.MotionDetected = detectionState === 'DETECTED' ? true : false; + this.MotionSensor.MotionDetected = detectionState === 'DETECTED' ? true : false; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -167,103 +121,86 @@ export class Motion { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // Movement - this.MotionDetected = this.BLE_MotionDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); - if (this.MotionDetected !== this.accessory.context.MotionDetected && this.MotionDetected) { + this.MotionSensor.MotionDetected = serviceData.movement!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor.MotionDetected}`); + if (this.MotionSensor.MotionDetected !== this.accessory.context.MotionDetected && this.MotionSensor.MotionDetected) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Detected Motion`); } // Light Level if (!this.device.motion?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - switch (this.BLE_CurrentAmbientLightLevel) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + switch (serviceData.lightLevel) { case 'dark': case 1: - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` + - ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); - if (this.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } } // Battery - if (this.BLE_BatteryLevel === undefined) { - this.BLE_BatteryLevel = 100; + if (serviceData.battery === undefined) { + serviceData.battery = 100; } - this.BatteryLevel = this.BLE_BatteryLevel!; - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = serviceData.battery!; + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`, + `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, ); } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // Motion State - this.MotionDetected = this.OpenAPI_MotionDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + this.MotionSensor.MotionDetected = deviceStatus.body.moveDetected!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor.MotionDetected}`); // Light Level if (!this.device.motion?.hide_lightsensor) { - this.set_minLux = this.minLux(); - this.set_maxLux = this.maxLux(); - switch (this.OpenAPI_CurrentAmbientLightLevel) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + switch (deviceStatus.body.brightness) { case 'dim': - this.CurrentAmbientLightLevel = this.set_minLux; + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; break; case 'bright': default: - this.CurrentAmbientLightLevel = this.set_maxLux; + this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` - + ` StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -289,115 +226,44 @@ export class Motion { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address - this.device.bleMac = - this.device.customBLEaddress || - this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); + this.device.bleMac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 's', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 's') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_MotionDetected = ad.serviceData.movement; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - await switchbot - .startScan({ - model: 's', - id: this.device.bleMac, - }) - .then(async () => { - return await this.retry({ - max: this.maxRetry(), - fn: async () => { - // Set an event handler - this.scanning = true; - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_MotionDetected = ad.serviceData.movement; - this.BLE_BatteryLevel = ad.serviceData.battery; - this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} movement: ${ad.serviceData.movement},` - + ` lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - this.debugErrorLog('1'); - await this.stopScanning(switchbot); - this.scanning = false; - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} scanning: ${this.scanning}`); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }, - }); - }) - .then(async () => { - // Stop to monitor - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} scanning: ${this.scanning}`); - if (this.scanning) { - this.debugErrorLog('2'); - await this.stopScanning(switchbot); - } - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - this.debugErrorLog('3'); - await this.BLERefreshConnection(switchbot); - }); - } else { - this.debugErrorLog('4'); + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -405,11 +271,7 @@ export class Motion { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_MotionDetected = deviceStatus.body.moveDetected; - this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -424,73 +286,49 @@ export class Motion { } } + /** * Updates the status for each of the HomeKit Characteristics */ async updateHomeKitCharacteristics(): Promise { - if (this.MotionDetected === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + if (this.MotionSensor.MotionDetected === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor.MotionDetected}`); } else { - this.accessory.context.MotionDetected = this.MotionDetected; - this.motionSensorService.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionDetected); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); + this.accessory.context.MotionDetected = this.MotionSensor.MotionDetected; + this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionSensor.MotionDetected); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` MotionDetected: ${this.MotionSensor.MotionDetected}`); } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } if (this.BLE) { - if (this.CurrentAmbientLightLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + if (this.LightSensor!.CurrentAmbientLightLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { - this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel; - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); + this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`, + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, ); } } } - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 's', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLERefreshConnection(switchbot: any): Promise { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + ` ${JSON.stringify(switchbot)}`); @@ -500,292 +338,38 @@ export class Motion { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - - minLux(): number { + async minLux(): Promise { + let set_minLux: number; if (this.device.motion?.set_minLux) { - this.set_minLux = this.device.motion!.set_minLux!; + set_minLux = this.device.motion!.set_minLux!; } else { - this.set_minLux = 1; + set_minLux = 1; } - return this.set_minLux; + return set_minLux; } - maxLux(): number { + async maxLux(): Promise { + let set_maxLux: number; if (this.device.motion?.set_maxLux) { - this.set_maxLux = this.device.motion!.set_maxLux!; + set_maxLux = this.device.motion!.set_maxLux!; } else { - this.set_maxLux = 6001; - } - return this.set_maxLux; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + set_maxLux = 6001; } + return set_maxLux; } async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); } } async apiError(e: any): Promise { - this.motionSensorService.updateCharacteristic(this.hap.Characteristic.MotionDetected, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); if (this.BLE) { - this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); } } - - async deviceContext() { - if (this.MotionDetected === undefined) { - this.MotionDetected = false; - } else { - this.MotionDetected = this.accessory.context.MotionDetected; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.motion) { - config = device.motion; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/device/plug.ts b/src/device/plug.ts index 1424a549..69c8d8b7 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -1,66 +1,30 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * plug.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { sleep } from '../utils.js'; -import { interval, Subject } from 'rxjs'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; - -export class Plug { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +export class Plug extends deviceBase { // Services - outletService: Service; - - // Characteristic Values - On!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // OpenAPI Others - OpenAPI_On: deviceStatus['power']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - - // BLE Others - BLE_IsConnected?: boolean; - BLE_On: serviceData['state']; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private Outlet: { + Service: Service; + On: CharacteristicValue; + }; // Updates plugUpdateInProgress!: boolean; doPlugUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doPlugUpdate = new Subject(); this.plugUpdateInProgress = false; @@ -68,29 +32,24 @@ export class Plug { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, this.model(device)) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Outlet property + this.Outlet = { + Service: this.accessory.addService(this.hap.Service.Battery), + On: accessory.context.On || false, + }; // get the Outlet service if it exists, otherwise create a new Outlet service // you can create multiple services for each accessory - const outletService = `${accessory.displayName} ${device.deviceType}`; - (this.outletService = accessory.getService(this.hap.Service.Outlet) - || accessory.addService(this.hap.Service.Outlet)), outletService; + const OutletService = `${accessory.displayName} ${device.deviceType}`; + (this.Outlet.Service = accessory.getService(this.hap.Service.Outlet) + || accessory.addService(this.hap.Service.Outlet)), OutletService; - this.outletService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.outletService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.outletService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.Outlet.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Outlet // create handlers for required characteristics - this.outletService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Outlet.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); // Update Homekit this.updateHomeKitCharacteristics(); @@ -109,12 +68,12 @@ export class Plug { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { powerState } = context; - const { On } = this; + const { On } = this.Outlet; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(powerState) = ' + `Webhook:(${powerState}), ` + `current:(${On})`); - this.On = powerState === 'ON' ? true : false; + this.Outlet.On = powerState === 'ON' ? true : false; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -146,51 +105,32 @@ export class Plug { }); } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // State - switch (this.BLE_On) { + switch (serviceData.state) { case 'on': - this.On = true; + this.Outlet.On = true; break; default: - this.On = false; + this.Outlet.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); } - async openAPIparseStatus() { + async openAPIparseStatus(deviceStatus: deviceStatus) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_On) { + switch (deviceStatus.body.power) { case 'on': - this.On = true; + this.Outlet.On = true; break; default: - this.On = false; + this.Outlet.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -224,86 +164,36 @@ export class Plug { this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { - await switchbot.startScan({ - model: this.BLEmodel(), - id: this.device.bleMac, - }); + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === this.BLEmodel()) { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_On = ad.serviceData.state; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: this.BLEmodel(), - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - this.BLE_On = ad.serviceData.state; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + - `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` + - `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } - async openAPIRefreshStatus() { + async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -311,8 +201,7 @@ export class Plug { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -361,8 +250,9 @@ export class Plug { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + if (this.Outlet.On !== this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` + + ` On: ${this.Outlet.On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -372,15 +262,15 @@ export class Plug { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); switchbot .discover({ - model: this.BLEmodel(), + model: this.device.bleModel, id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (this.Outlet.On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -391,8 +281,8 @@ export class Plug { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.On = false; + + `On: ${this.Outlet.On} sent over BLE, sent successfully`); + this.Outlet.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -403,17 +293,16 @@ export class Plug { await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` + + ` On: ${this.Outlet.On}, OnCached: ${this.accessory.context.On}`); } } async openAPIpushChanges() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.On !== this.accessory.context.On) { + if (this.Outlet.On !== this.accessory.context.On) { let command = ''; - if (this.On) { + if (this.Outlet.On) { command = 'turnOn'; } else { command = 'turnOff'; @@ -439,24 +328,19 @@ export class Plug { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + + `On: ${this.Outlet.On}, OnCached: ${this.accessory.context.On}`); } } @@ -464,60 +348,24 @@ export class Plug { * Handle requests to set the value of the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - if (this.On === this.accessory.context.On) { + if (this.Outlet.On === this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); } - this.On = value; + this.Outlet.On = value; this.doPlugUpdate.next(); } async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - } else { - this.accessory.context.On = this.On; - this.outletService.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); + if (this.Outlet.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); } else { - await this.BLERefreshConnection(switchbot); - } - } - - BLEmodel(): 'g' | 'j' { - if (this.device.deviceType === 'Plug Mini (US)' || this.device.configDeviceType === 'Plug Mini (US)') { - return 'g'; - } else { - return 'j'; - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: this.BLEmodel(), - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.On = this.Outlet.On; + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet.On}`); } } @@ -537,285 +385,13 @@ export class Plug { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - - model(device: device & devicesConfig): string { - let model: string; - if (device.deviceType === 'Plug Mini (US)') { - model = 'W1901400'; - model = 'W1901401'; - } else if (device.deviceType === 'Plug Mini (JP)') { - model = 'W2001400'; - } else { - model = 'SP11'; - } - return model; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } async apiError(e: any): Promise { - this.outletService.updateCharacteristic(this.hap.Characteristic.On, e); - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.plug) { - config = device.plug; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, e); } } diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index fa1c4794..f2c2b78f 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -1,73 +1,39 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * robotvacuumcleaner.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; -import { sleep } from '../utils.js'; -import { interval, Subject } from 'rxjs'; +import { Subject, interval, skipWhile } from 'rxjs'; +import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; -import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js'; - -export class RobotVacuumCleaner { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +import { debounceTime, take, tap } from 'rxjs/operators'; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; + +export class RobotVacuumCleaner extends deviceBase { // Services - batteryService: Service; - robotVacuumCleanerService: Service; - - // Characteristic Values - On!: CharacteristicValue; - Brightness!: CharacteristicValue; - BatteryLevel!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_On: deviceStatus['power']; - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - - // BLE Status - BLE_On: serviceData['state']; - - // BLE Others - BLE_IsConnected?: boolean; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private LightBulb: { + Service: Service; + On: CharacteristicValue; + Brightness: CharacteristicValue; + }; + + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; // Updates robotVacuumCleanerUpdateInProgress!: boolean; doRobotVacuumCleanerUpdate!: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); - this.deviceRetry(device); - this.deviceConfig(device); - + super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doRobotVacuumCleanerUpdate = new Subject(); this.robotVacuumCleanerUpdateInProgress = false; @@ -75,32 +41,35 @@ export class RobotVacuumCleaner { // Retrieve initial values and updateHomekit this.refreshStatus(); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, this.model(device)) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Lightbulb property + this.LightBulb = { + Service: this.accessory.addService(this.hap.Service.Lightbulb), + On: accessory.context.On || false, + Brightness: accessory.context.Brightness || 0, + }; + + // Initialize Battery property + this.Battery = { + Service: this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory - const robotVacuumCleanerService = `${accessory.displayName} ${device.deviceType}`; - (this.robotVacuumCleanerService = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), robotVacuumCleanerService; + const LightBulbService = `${accessory.displayName} ${device.deviceType}`; + (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) + || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - this.robotVacuumCleanerService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.robotVacuumCleanerService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.robotVacuumCleanerService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Lightbulb // create handlers for required characteristics - this.robotVacuumCleanerService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); // handle Brightness events using the Brightness characteristic - this.robotVacuumCleanerService + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ minStep: 25, @@ -110,19 +79,16 @@ export class RobotVacuumCleaner { validValueRanges: [0, 100], }) .onGet(() => { - return this.Brightness; + return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); // Update Homekit this.updateHomeKitCharacteristics(); @@ -141,13 +107,14 @@ export class RobotVacuumCleaner { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { onlineStatus, battery } = context; - const { On, BatteryLevel } = this; + const { On } = this.LightBulb; + const { BatteryLevel } = this.Battery; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(onlineStatus, battery) = ' + `Webhook:(${onlineStatus}, ${battery}), ` + `current:(${On}, ${BatteryLevel})`); - this.On = onlineStatus === 'online' ? true : false; - this.BatteryLevel = battery; + this.LightBulb.On = onlineStatus === 'online' ? true : false; + this.Battery.BatteryLevel = battery; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -167,10 +134,10 @@ export class RobotVacuumCleaner { ) .subscribe(async () => { try { - if (this.On !== this.accessory.context.On) { + if (this.LightBulb.On !== this.accessory.context.On) { await this.pushChanges(); } - if (this.On && this.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.On && this.LightBulb.Brightness !== this.accessory.context.Brightness) { await this.openAPIpushBrightnessChanges(); } } catch (e: any) { @@ -184,60 +151,55 @@ export class RobotVacuumCleaner { }); } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - /*} else if (this.BLE) { - await this.BLEparseStatus();*/ - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); + async BLEparseStatus(serviceData: serviceData): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); + + // Battery + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - } + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); - async BLEparseStatus(): Promise { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); // State - switch (this.BLE_On) { + switch (serviceData.state) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } - async openAPIparseStatus() { + async openAPIparseStatus(deviceStatus: deviceStatus) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); - switch (this.OpenAPI_On) { + switch (deviceStatus.body.power) { case 'on': - this.On = true; + this.LightBulb.On = true; break; default: - this.On = false; + this.LightBulb.On = false; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - // Battery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + // BatteryLevel + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } /** @@ -281,7 +243,6 @@ export class RobotVacuumCleaner { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); if (this.device.bleMac === ad.address && ad.serviceData.model === '?') { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_On = ad.serviceData.state; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } @@ -291,7 +252,7 @@ export class RobotVacuumCleaner { // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); /*if (switchbot !== false) { @@ -307,7 +268,7 @@ export class RobotVacuumCleaner { `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + ` BLE Address Found: ${ad.address}`, ); - this.BLE_On = ad.serviceData.state; + serviceData.state = ad.serviceData.state; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + @@ -347,7 +308,7 @@ export class RobotVacuumCleaner { async openAPIRefreshStatus() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders(), }); @@ -358,10 +319,7 @@ export class RobotVacuumCleaner { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_On = deviceStatus.body.power; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -409,8 +367,9 @@ export class RobotVacuumCleaner { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); - if (this.On !== this.accessory.context.On) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`); + if (this.LightBulb.On !== this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` + + ` On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -424,11 +383,11 @@ export class RobotVacuumCleaner { id: this.device.bleMac, }) .then(async (device_list: any) => { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); - return await this.retry({ - max: this.maxRetry(), + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { + if (this.LightBulb.On) { return await device_list[0].turnOn({ id: this.device.bleMac }); } else { return await device_list[0].turnOff({ id: this.device.bleMac }); @@ -439,8 +398,8 @@ export class RobotVacuumCleaner { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.On} sent over BLE, sent successfully`); - this.On = false; + + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); + this.LightBulb.On = false; }) .catch(async (e: any) => { this.apiError(e); @@ -451,26 +410,15 @@ export class RobotVacuumCleaner { await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` + + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); } } async openAPIpushChanges() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); - if (this.On !== this.accessory.context.On) { - let command = ''; - if (this.On) { - command = 'turnOn'; - } else { - command = 'turnOff'; - } - const bodyChange = JSON.stringify({ - command: `${command}`, - parameter: 'default', - commandType: 'command', - }); + if (this.LightBulb.On !== this.accessory.context.On) { + const bodyChange = await this.commands(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); try { const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { @@ -502,7 +450,7 @@ export class RobotVacuumCleaner { } else { this.debugLog( `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.On}, ` + + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, ); } @@ -510,7 +458,7 @@ export class RobotVacuumCleaner { async openAPIpushBrightnessChanges() { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushBrightnessChanges`); - const bodyChange = this.brightnessCommands(); + const bodyChange = await this.brightnessCommands(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); try { const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { @@ -540,10 +488,10 @@ export class RobotVacuumCleaner { } } - private commands() { + async commands() { let command: string; let parameter: string; - if (this.On) { + if (this.LightBulb.On) { command = 'start'; parameter = 'default'; } else { @@ -558,19 +506,19 @@ export class RobotVacuumCleaner { return body; } - private brightnessCommands() { + async brightnessCommands(): Promise { let command: string; let parameter: string; - if (this.Brightness === 25) { + if (this.LightBulb.Brightness === 25) { command = 'PowLevel'; parameter = '0'; - } else if (this.Brightness === 50) { + } else if (this.LightBulb.Brightness === 50) { command = 'PowLevel'; parameter = '1'; - } else if (this.Brightness === 75) { + } else if (this.LightBulb.Brightness === 75) { command = 'PowLevel'; parameter = '2'; - } else if (this.Brightness === 100) { + } else if (this.LightBulb.Brightness === 100) { command = 'PowLevel'; parameter = '3'; } else { @@ -589,13 +537,13 @@ export class RobotVacuumCleaner { * Handle requests to set the value of the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - if (this.On === this.accessory.context.On) { + if (this.LightBulb.On === this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); } else { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); } - this.On = value; + this.LightBulb.On = value; this.doRobotVacuumCleanerUpdate.next(); } @@ -603,78 +551,51 @@ export class RobotVacuumCleaner { * Handle requests to set the value of the "Brightness" characteristic */ async BrightnessSet(value: CharacteristicValue): Promise { - if (this.Brightness === this.accessory.context.Brightness) { + if (this.LightBulb.Brightness === this.accessory.context.Brightness) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`); - } else if (this.On) { + } else if (this.LightBulb.On) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); } - this.Brightness = value; + this.LightBulb.Brightness = value; this.doRobotVacuumCleanerUpdate.next(); } async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.LightBulb.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); } else { - this.accessory.context.On = this.On; - this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.LightBulb.On; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`); } // Brightness - if (this.Brightness === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + if (this.LightBulb.Brightness === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); } else { - this.accessory.context.Brightness = this.Brightness; - this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); + this.accessory.context.Brightness = this.LightBulb.Brightness; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`); } // BatteryLevel - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // StatusLowBattery - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); - } - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: '?', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } @@ -694,289 +615,13 @@ export class RobotVacuumCleaner { } } - async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise { - return fn().catch(async (e: any) => { - if (max === 0) { - throw e; - } - this.infoLog(e); - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`); - await sleep(1000); - return this.retry({ max: max - 1, fn }); - }); - } - - maxRetry(): number { - if (this.device.maxRetry) { - return this.device.maxRetry; - } else { - return 5; - } - } - - model(device: device & devicesConfig): string { - let model: string; - if (device.deviceType === 'Robot Vacuum Cleaner S1 Plus') { - model = 'W3011010'; - } else if (device.deviceType === 'Robot Vacuum Cleaner S1') { - model = 'W3011000'; - } else { - model = 'N/A'; - } - return model; - } - - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); } } async apiError(e: any): Promise { - this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.On, e); - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.Brightness === undefined) { - this.Brightness = 0; - } else { - this.Brightness = this.accessory.context.Brightness; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.plug) { - config = device.plug; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.offline !== undefined) { - config['offline'] = device.offline; - } - if (device.maxRetry !== undefined) { - config['maxRetry'] = device.maxRetry; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, e); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 3970215a..ae993415 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -1,89 +1,46 @@ -import { hostname } from 'os'; -import { sleep } from '../utils.js'; -import { MqttClient } from 'mqtt'; -import asyncmqtt from 'async-mqtt'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * waterdetector.ts: @switchbot/homebridge-switchbot. + */ +import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js'; +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class WaterDetector { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class WaterDetector extends deviceBase { // Services - leakService?: Service; - batteryService: Service; - - // Characteristic Values - StatusActive!: CharacteristicValue; - LeakDetected!: CharacteristicValue; - BatteryLevel!: CharacteristicValue; - ChargingState!: CharacteristicValue; - StatusLowBattery!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // OpenAPI Status - OpenAPI_BatteryLevel: deviceStatus['battery']; - OpenAPI_FirmwareRevision: deviceStatus['version']; - OpenAPI_LeakDetected: deviceStatus['status']; - - // BLE Status - BLE_BatteryLevel!: serviceData['battery']; - BLE_LeakDetected!: number;//serviceData['status']; - - // BLE Others - BLE_IsConnected?: boolean; - - //MQTT stuff - mqttClient: MqttClient | null = null; - - // EVE history service handler - historyService?: any; - - // Config - scanDuration!: number; - deviceLogging!: string; - deviceRefreshRate!: number; - maxRetries!: number; - delayBetweenRetries!: number; + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + ChargingState: CharacteristicValue; + }; + + private LeakSensor?: { + Service: Service; + StatusActive: CharacteristicValue; + LeakDetected: CharacteristicValue; + }; // Updates WaterDetectorUpdateInProgress!: boolean; doWaterDetectorUpdate: Subject; - // Connection - private readonly OpenAPI: boolean; - private readonly BLE: boolean; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: device & devicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // Connection - this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; - this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; + super(platform, accessory, device); // default placeholders - this.deviceLogs(device); - this.scan(device); - this.refreshRate(device); - this.deviceContext(); this.setupHistoryService(device); - this.setupMqtt(device); - this.deviceRetry(device); - this.deviceConfig(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doWaterDetectorUpdate = new Subject(); @@ -92,6 +49,23 @@ export class WaterDetector { // Retrieve initial values and updateHomekit this.refreshStatus(); + // Initialize Battery property + this.Battery = { + Service: this.accessory.getService(this.hap.Service.Battery) || this.accessory.addService(this.hap.Service.Battery), + BatteryLevel: this.accessory.context.BatteryLevel || 100, + StatusLowBattery: this.accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: this.accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, + }; + + // Initialize Leak Sensor property + if (!this.device.waterdetector?.hide_leak) { + this.LeakSensor = { + Service: this.accessory.getService(this.hap.Service.LeakSensor) || this.accessory.addService(this.hap.Service.LeakSensor), + StatusActive: this.accessory.context.StatusActive || false, + LeakDetected: this.accessory.context.LeakDetected || this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, + }; + } + // set accessory information accessory .getService(this.hap.Service.AccessoryInformation)! @@ -103,32 +77,26 @@ export class WaterDetector { // Leak Sensor Service if (device.waterdetector?.hide_leak) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); - this.leakService = this.accessory.getService(this.hap.Service.LeakSensor); - accessory.removeService(this.leakService!); - } else if (!this.leakService) { + this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; + accessory.removeService(this.LeakSensor!.Service); + } else if (!this.LeakSensor!.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Leak Sensor Service`); - const leakService = `${accessory.displayName} Leak Sensor`; - (this.leakService = this.accessory.getService(this.hap.Service.LeakSensor) - || this.accessory.addService(this.hap.Service.LeakSensor)), leakService; + const LeakSensorService = `${accessory.displayName} Leak Sensor`; + (this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) + || this.accessory.addService(this.hap.Service.LeakSensor)), LeakSensorService; - this.leakService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Leak Sensor`); - if (!this.leakService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.leakService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Leak Sensor`); - } + this.LeakSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LeakSensorService); } else { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Leak Sensor Service Not Added`); } // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.batteryService = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + const BatteryService = `${accessory.displayName} Battery`; + (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) + || accessory.addService(this.hap.Service.Battery)), BatteryService; - this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`); - } - this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -141,18 +109,18 @@ export class WaterDetector { }); //regisiter webhook event handler - if (this.device.webhook) { + if (this.device.webhook && !this.device.waterdetector?.hide_leak) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { status } = context; - const { LeakDetected } = this; + const { LeakDetected } = this.LeakSensor!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(status) = ' + - `Webhook:(${status}), ` + - `current:(${LeakDetected})`); - this.LeakDetected = status; + '(status) = ' + + `Webhook:(${status}), ` + + `current:(${LeakDetected})`); + this.LeakSensor!.LeakDetected = status; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -162,70 +130,51 @@ export class WaterDetector { } } - /** - * Parse the device status from the SwitchBot api - */ - async parseStatus(): Promise { - if (!this.device.enableCloudService && this.OpenAPI) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`); - } else if (this.BLE) { - await this.BLEparseStatus(); - } else if (this.OpenAPI && this.platform.config.credentials?.token) { - await this.openAPIparseStatus(); - } else { - await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`, - ); - } - } - - async BLEparseStatus(): Promise { + async BLEparseStatus(serviceData: serviceData): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); - // Battery - this.BatteryLevel = Number(this.BLE_BatteryLevel); - if (this.BatteryLevel < 15) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(serviceData.battery); + if (this.Battery.BatteryLevel < 15) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); // LeakDetected - this.LeakDetected = this.BLE_LeakDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); + if (this.device.waterdetector?.hide_leak) { + this.LeakSensor!.LeakDetected = serviceData.status!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakSensor!.LeakDetected}`); + } } - async openAPIparseStatus(): Promise { + async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // StatusLowBattery - this.BatteryLevel = Number(this.OpenAPI_BatteryLevel); - if (this.BatteryLevel < 10) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`); // BatteryLevel - if (Number.isNaN(this.BatteryLevel)) { - this.BatteryLevel = 100; + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); // LeakDetected - this.LeakDetected = this.OpenAPI_LeakDetected!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); + if (!this.device.waterdetector?.hide_leak) { + this.LeakSensor!.LeakDetected = deviceStatus.body.status!; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakSensor!.LeakDetected}`); + } // FirmwareRevision - this.FirmwareRevision = this.OpenAPI_FirmwareRevision!; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; + this.accessory.context.FirmwareRevision = deviceStatus.body.version; } - /** - * Asks the SwitchBot API for the latest device information - */ async refreshStatus(): Promise { if (!this.device.enableCloudService && this.OpenAPI) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`); @@ -255,39 +204,35 @@ export class WaterDetector { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'l', - id: this.device.bleMac, - }); + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === 'l') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.BLE_LeakDetected = ad.serviceData.status; - this.BLE_BatteryLevel = ad.serviceData.battery; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); // Update HomeKit - await this.BLEparseStatus(); + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); + if (switchbot === undefined) { + await this.BLERefreshConnection(switchbot); + } } async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { - const { body, statusCode } = await this.platform.retryRequest(this.maxRetries, this.delayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -295,10 +240,7 @@ export class WaterDetector { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.OpenAPI_LeakDetected = deviceStatus.body.status; - this.OpenAPI_BatteryLevel = deviceStatus.body.battery; - this.OpenAPI_FirmwareRevision = deviceStatus.body.version; - this.openAPIparseStatus(); + this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -320,129 +262,54 @@ export class WaterDetector { const mqttmessage: string[] = []; const entry = { time: Math.round(new Date().valueOf() / 1000) }; if (!this.device.waterdetector?.hide_leak) { - if (this.LeakDetected === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakDetected}`); + if (this.LeakSensor!.LeakDetected === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakSensor!.LeakDetected}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"LeakDetected": ${this.LeakDetected}`); + mqttmessage.push(`"LeakDetected": ${this.LeakSensor!.LeakDetected}`); } if (this.device.history) { - entry['leak'] = this.LeakDetected; + entry['leak'] = this.LeakSensor!.LeakDetected; } - this.accessory.context.LeakDetected = this.LeakDetected; - this.leakService?.updateCharacteristic(this.hap.Characteristic.LeakDetected, this.LeakDetected); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LeakDetected: ${this.LeakDetected}`); + this.accessory.context.LeakDetected = this.LeakSensor!.LeakDetected; + this.LeakSensor!.Service.updateCharacteristic(this.hap.Characteristic.LeakDetected, this.LeakSensor!.LeakDetected); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LeakDetected: ${this.LeakSensor!.LeakDetected}`); } } - if (this.BatteryLevel === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + if (this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"battery": ${this.BatteryLevel}`); + mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`); } - this.accessory.context.BatteryLevel = this.BatteryLevel; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } - if (this.StatusLowBattery === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + if (this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { if (this.device.mqttURL) { - mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`); + mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } if (this.device.mqttURL) { this.mqttPublish(`{${mqttmessage.join(',')}}`); } - if (Number(this.LeakDetected) > 0) { - // reject unreliable data - if (this.device.history) { - this.historyService?.addEntry(entry); - } - } - } - - /* - * Publish MQTT message for topics of - * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx' - */ - mqttPublish(message: any) { - const mac = this.device.deviceId - ?.toLowerCase() - .match(/[\s\S]{1,2}/g) - ?.join(':'); - const options = this.device.mqttPubOptions || {}; - this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`); - } - - /* - * Setup MQTT hadler if URL is specified. - */ - async setupMqtt(device: device & devicesConfig): Promise { - if (device.mqttURL) { - try { - const { connectAsync } = asyncmqtt; - this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); - this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); - }); - } catch (e) { - this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + if (!this.device.waterdetector?.hide_leak) { + if (Number(this.LeakSensor!.LeakDetected) > 0) { + // reject unreliable data + if (this.device.history) { + this.historyService?.addEntry(entry); + } } } } - /* - * Setup EVE history graph feature if enabled. - */ - async setupHistoryService(device: device & devicesConfig): Promise { - const mac = this.device - .deviceId!.match(/.{1,2}/g)! - .join(':') - .toLowerCase(); - this.historyService = device.history - ? new this.platform.fakegatoAPI('room', this.accessory, { - log: this.platform.log, - storage: 'fs', - filename: `${hostname().split('.')[0]}_${mac}_persist.json`, - }) - : null; - } - - async stopScanning(switchbot: any) { - switchbot.stopScan(); - if (this.BLE_IsConnected) { - await this.BLEparseStatus(); - await this.updateHomeKitCharacteristics(); - } else { - await this.BLERefreshConnection(switchbot); - } - } - - async getCustomBLEAddress(switchbot: any) { - if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) { - (async () => { - // Start to monitor advertisement packets - await switchbot.startScan({ - model: 'i', - }); - // Set an event handler - switchbot.onadvertisement = (ad: any) => { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`); - }; - await sleep(10000); - // Stop to monitor - switchbot.stopScan(); - })(); - } - } - async BLERefreshConnection(switchbot: any): Promise { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + ` ${JSON.stringify(switchbot)}`); @@ -452,264 +319,17 @@ export class WaterDetector { } } - async scan(device: device & devicesConfig): Promise { - if (device.scanDuration) { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); - } - } else { - this.scanDuration = this.accessory.context.scanDuration = 1; - if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); - } - } - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - this.offlineOff(); - break; - case 171: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - this.offlineOff(); - break; - case 190: - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async offlineOff(): Promise { - if (this.device.offline) { - await this.deviceContext(); - await this.updateHomeKitCharacteristics(); + if (this.device.offline && !this.device.waterdetector?.hide_leak) { + this.LeakSensor!.Service.updateCharacteristic(this.hap.Characteristic.LeakDetected, this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED); } } async apiError(e: any): Promise { if (!this.device.waterdetector?.hide_leak) { - this.leakService?.updateCharacteristic(this.hap.Characteristic.LeakDetected, e); - } - this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - - async deviceContext() { - if (this.LeakDetected === undefined) { - this.LeakDetected = 0; - } else { - this.LeakDetected = this.accessory.context.LeakDetected; - } - if (this.BatteryLevel === undefined) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = this.accessory.context.BatteryLevel; - } - if (this.StatusLowBattery === undefined) { - this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; - this.accessory.context.StatusLowBattery = this.StatusLowBattery; - } else { - this.StatusLowBattery = this.accessory.context.StatusLowBattery; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async refreshRate(device: device & devicesConfig): Promise { - if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); - } - } - - async deviceRetry(device: device & devicesConfig): Promise { - if (!device.maxRetries) { - this.maxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.maxRetries}`); - } else { - this.maxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.maxRetries}`); - } - if (!device.delayBetweenRetries) { - this.delayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set, Using: ${this.delayBetweenRetries}`); - } else { - this.delayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.delayBetweenRetries}`); - } - } - - async deviceConfig(device: device & devicesConfig): Promise { - let config = {}; - if (device.waterdetector) { - config = device.waterdetector; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; - } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; - } - if (device.webhook !== undefined) { - config['webhook'] = device.webhook; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + this.LeakSensor!.Service.updateCharacteristic(this.hap.Characteristic.LeakDetected, e); } - } - - async deviceLogs(device: device & devicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); } } diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index f254605a..dccc6ee8 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -1,25 +1,25 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * airconditioners.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class AirConditioner { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class AirConditioner extends irdeviceBase { // Services coolerService!: Service; // Characteristic Values Active!: CharacteristicValue; RotationSpeed!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; ThresholdTemperature!: CharacteristicValue; CurrentRelativeHumidity?: CharacteristicValue; @@ -38,44 +38,94 @@ export class AirConditioner { static MODE_HEAT: number; // Config - deviceLogging!: string; hide_automode?: boolean; set_max_heat?: number; set_min_heat?: number; set_max_cool?: number; set_min_cool?: number; - disablePushOn?: boolean; - disablePushOff?: boolean; meter?: PlatformAccessory; - disablePushDetail?: boolean; private readonly valid12 = [1, 2]; private readonly valid012 = [0, 1, 2]; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.disablePushDetailChanges(device); - this.deviceConfig(device); - - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + super(platform, accessory, device); + + if (this.Active === undefined) { + this.Active = this.hap.Characteristic.Active.INACTIVE; + } else if (this.Active) { + this.Active; + } else { + this.Active = this.accessory.context.Active; + } + + if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; + } + + if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { + this.ThresholdTemperature = 24; + } else { + this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; + } + + if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { + this.RotationSpeed = 4; + } else { + this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; + } + + if (this.device.irair?.hide_automode) { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } else { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } + + if (this.device.irair?.set_max_heat) { + this.set_max_heat = this.device.irair?.set_max_heat; + this.accessory.context.set_max_heat = this.set_max_heat; + } else { + this.set_max_heat = 35; + this.accessory.context.set_max_heat = this.set_max_heat; + } + if (this.device.irair?.set_min_heat) { + this.set_min_heat = this.device.irair?.set_min_heat; + this.accessory.context.set_min_heat = this.set_min_heat; + } else { + this.set_min_heat = 0; + this.accessory.context.set_min_heat = this.set_min_heat; + } + + if (this.device.irair?.set_max_cool) { + this.set_max_cool = this.device.irair?.set_max_cool; + this.accessory.context.set_max_cool = this.set_max_cool; + } else { + this.set_max_cool = 35; + this.accessory.context.set_max_cool = this.set_max_cool; + } + if (this.device.irair?.set_min_cool) { + this.set_min_cool = this.device.irair?.set_min_cool; + this.accessory.context.set_min_cool = this.set_min_cool; + } else { + this.set_min_cool = 0; + this.accessory.context.set_min_cool = this.set_min_cool; + } + + if (this.meter) { + if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { + this.CurrentRelativeHumidity = 0; + } else { + this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; + } + } // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory @@ -84,9 +134,6 @@ export class AirConditioner { || accessory.addService(this.hap.Service.HeaterCooler)), coolerService; this.coolerService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.coolerService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.coolerService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } // handle on / off events using the Active characteristic this.coolerService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); @@ -243,7 +290,78 @@ export class AirConditioner { private async UpdateCurrentHeaterCoolerState() { if (this.Active === this.hap.Characteristic.Active.ACTIVE) { - await this.deviceContext(); + + if (this.Active === undefined) { + this.Active = this.hap.Characteristic.Active.INACTIVE; + } else if (this.Active) { + this.Active; + } else { + this.Active = this.accessory.context.Active; + } + + if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; + } + + if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { + this.ThresholdTemperature = 24; + } else { + this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; + } + + if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { + this.RotationSpeed = 4; + } else { + this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; + } + + if (this.device.irair?.hide_automode) { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } else { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } + + if (this.device.irair?.set_max_heat) { + this.set_max_heat = this.device.irair?.set_max_heat; + this.accessory.context.set_max_heat = this.set_max_heat; + } else { + this.set_max_heat = 35; + this.accessory.context.set_max_heat = this.set_max_heat; + } + if (this.device.irair?.set_min_heat) { + this.set_min_heat = this.device.irair?.set_min_heat; + this.accessory.context.set_min_heat = this.set_min_heat; + } else { + this.set_min_heat = 0; + this.accessory.context.set_min_heat = this.set_min_heat; + } + + if (this.device.irair?.set_max_cool) { + this.set_max_cool = this.device.irair?.set_max_cool; + this.accessory.context.set_max_cool = this.set_max_cool; + } else { + this.set_max_cool = 35; + this.accessory.context.set_max_cool = this.set_max_cool; + } + if (this.device.irair?.set_min_cool) { + this.set_min_cool = this.device.irair?.set_min_cool; + this.accessory.context.set_min_cool = this.set_min_cool; + } else { + this.set_min_cool = 0; + this.accessory.context.set_min_cool = this.set_min_cool; + } + + if (this.meter) { + if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { + this.CurrentRelativeHumidity = 0; + } else { + this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; + } + } if (this.ThresholdTemperature < this.CurrentTemperature && this.TargetHeaterCoolerState !== this.hap.Characteristic.TargetHeaterCoolerState.HEAT) { this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING; @@ -440,7 +558,78 @@ export class AirConditioner { } async ThresholdTemperatureGet(): Promise { - await this.deviceContext(); + + if (this.Active === undefined) { + this.Active = this.hap.Characteristic.Active.INACTIVE; + } else if (this.Active) { + this.Active; + } else { + this.Active = this.accessory.context.Active; + } + + if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; + } + + if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { + this.ThresholdTemperature = 24; + } else { + this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; + } + + if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { + this.RotationSpeed = 4; + } else { + this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; + } + + if (this.device.irair?.hide_automode) { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } else { + this.hide_automode = this.device.irair?.hide_automode; + this.accessory.context.hide_automode = this.hide_automode; + } + + if (this.device.irair?.set_max_heat) { + this.set_max_heat = this.device.irair?.set_max_heat; + this.accessory.context.set_max_heat = this.set_max_heat; + } else { + this.set_max_heat = 35; + this.accessory.context.set_max_heat = this.set_max_heat; + } + if (this.device.irair?.set_min_heat) { + this.set_min_heat = this.device.irair?.set_min_heat; + this.accessory.context.set_min_heat = this.set_min_heat; + } else { + this.set_min_heat = 0; + this.accessory.context.set_min_heat = this.set_min_heat; + } + + if (this.device.irair?.set_max_cool) { + this.set_max_cool = this.device.irair?.set_max_cool; + this.accessory.context.set_max_cool = this.set_max_cool; + } else { + this.set_max_cool = 35; + this.accessory.context.set_max_cool = this.set_max_cool; + } + if (this.device.irair?.set_min_cool) { + this.set_min_cool = this.device.irair?.set_min_cool; + this.accessory.context.set_min_cool = this.set_min_cool; + } else { + this.set_min_cool = 0; + this.accessory.context.set_min_cool = this.set_min_cool; + } + + if (this.meter) { + if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { + this.CurrentRelativeHumidity = 0; + } else { + this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; + } + } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get ThresholdTemperature: ${this.ThresholdTemperature}`); return this.ThresholdTemperature; } @@ -525,136 +714,6 @@ export class AirConditioner { } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async disablePushDetailChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushDetail === undefined) { - this.disablePushDetail = false; - } else { - this.disablePushDetail = device.disablePushDetail; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError({ e }: { e: any }): Promise { this.coolerService.updateCharacteristic(this.hap.Characteristic.Active, e); this.coolerService.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); @@ -665,188 +724,4 @@ export class AirConditioner { this.coolerService.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, e); this.coolerService.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, e); } - - async deviceContext(): Promise { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else if (this.Active) { - this.Active; - } else { - this.Active = this.accessory.context.Active; - } - - if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; - } else { - this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; - } - - if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { - this.ThresholdTemperature = 24; - } else { - this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; - } - - if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { - this.RotationSpeed = 4; - } else { - this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; - } - - if (this.device.irair?.hide_automode) { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } else { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } - - if (this.device.irair?.set_max_heat) { - this.set_max_heat = this.device.irair?.set_max_heat; - this.accessory.context.set_max_heat = this.set_max_heat; - } else { - this.set_max_heat = 35; - this.accessory.context.set_max_heat = this.set_max_heat; - } - if (this.device.irair?.set_min_heat) { - this.set_min_heat = this.device.irair?.set_min_heat; - this.accessory.context.set_min_heat = this.set_min_heat; - } else { - this.set_min_heat = 0; - this.accessory.context.set_min_heat = this.set_min_heat; - } - - if (this.device.irair?.set_max_cool) { - this.set_max_cool = this.device.irair?.set_max_cool; - this.accessory.context.set_max_cool = this.set_max_cool; - } else { - this.set_max_cool = 35; - this.accessory.context.set_max_cool = this.set_max_cool; - } - if (this.device.irair?.set_min_cool) { - this.set_min_cool = this.device.irair?.set_min_cool; - this.accessory.context.set_min_cool = this.set_min_cool; - } else { - this.set_min_cool = 0; - this.accessory.context.set_min_cool = this.set_min_cool; - } - - if (this.meter) { - if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; - } else { - this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; - } - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irair) { - config = device.irair; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (device.disablePushDetail !== undefined) { - config['disablePushDetail'] = device.disablePushDetail; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging() && this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging() && this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 62283a24..e7c70f10 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -1,18 +1,19 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * airpurifier.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class AirPurifier { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class AirPurifier extends irdeviceBase { // Services airPurifierService!: Service; @@ -22,7 +23,6 @@ export class AirPurifier { CurrentAPTemp!: CharacteristicValue; CurrentAPMode!: CharacteristicValue; RotationSpeed!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; CurrentAPFanSpeed!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; CurrentAirPurifierState!: CharacteristicValue; @@ -38,34 +38,24 @@ export class AirPurifier { CurrentFanSpeed!: number; static PURIFYING_AIR: number; - // Config - disablePushOn?: boolean; - disablePushOff?: boolean; - deviceLogging!: string; - constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // default placeholders + if (this.Active === undefined) { + this.Active = this.hap.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } + if (this.CurrentTemperature === undefined) { + this.CurrentTemperature = 24; + } else { + this.CurrentTemperature = this.accessory.context.CurrentTemperature; + } // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory @@ -74,9 +64,6 @@ export class AirPurifier { || accessory.addService(this.hap.Service.AirPurifier)), airPurifierService; this.airPurifierService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.airPurifierService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.airPurifierService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } // handle on / off events using the Active characteristic this.airPurifierService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); @@ -280,256 +267,10 @@ export class AirPurifier { } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError(e: any): Promise { this.airPurifierService.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e); this.airPurifierService.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, e); this.airPurifierService.updateCharacteristic(this.hap.Characteristic.TargetAirPurifierState, e); this.airPurifierService.updateCharacteristic(this.hap.Characteristic.Active, e); } - - private deviceContext() { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; - } else { - this.CurrentTemperature = this.accessory.context.CurrentTemperature; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irpur) { - config = device.irpur; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 0a781c69..19755167 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -1,53 +1,38 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * camera.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Camera { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Camera extends irdeviceBase { // Services switchService!: Service; // Characteristic Values On!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // Config - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // default placeholders + if (this.On === undefined) { + this.On = false; + } else { + this.On = accessory.context.On; + } // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory @@ -56,9 +41,6 @@ export class Camera { || accessory.addService(this.hap.Service.Switch)), switchService; this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.switchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.switchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } // handle on / off events using the On characteristic this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); @@ -164,248 +146,7 @@ export class Camera { } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError(e: any): Promise { this.switchService.updateCharacteristic(this.hap.Characteristic.On, e); } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.ircam) { - config = device.ircam; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 83eba172..4e8545a3 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -1,18 +1,19 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * fan.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Fan { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Fan extends irdeviceBase { // Services fanService!: Service; @@ -20,7 +21,6 @@ export class Fan { Active!: CharacteristicValue; SwingMode!: CharacteristicValue; RotationSpeed!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; ActiveIdentifier!: CharacteristicValue; RotationDirection!: CharacteristicValue; @@ -28,33 +28,20 @@ export class Fan { minStep?: number; minValue?: number; maxValue?: number; - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // default placeholders + if (this.Active === undefined) { + this.Active = this.hap.Characteristic.Active.INACTIVE; + } else { + this.Active = this.accessory.context.Active; + } // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory @@ -63,9 +50,6 @@ export class Fan { || accessory.addService(this.hap.Service.Fanv2)), fanService; this.fanService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.fanService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.fanService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } // handle on / off events using the Active characteristic this.fanService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); @@ -294,250 +278,9 @@ export class Fan { } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError(e: any): Promise { this.fanService.updateCharacteristic(this.hap.Characteristic.Active, e); this.fanService.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); this.fanService.updateCharacteristic(this.hap.Characteristic.SwingMode, e); } - - async deviceContext() { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irfan) { - config = device.irfan; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts new file mode 100644 index 00000000..cf8a20b3 --- /dev/null +++ b/src/irdevice/irdevice.ts @@ -0,0 +1,518 @@ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * device.ts: homebridge-august. + */ +import { API, HAP, Logging, PlatformAccessory } from 'homebridge'; + +import { SwitchBotPlatform } from '../platform.js'; +import { SwitchBotPlatformConfig, irDevicesConfig, irdevice } from '../settings.js'; + +export abstract class irdeviceBase { + public readonly api: API; + public readonly log: Logging; + public readonly config!: SwitchBotPlatformConfig; + protected readonly hap: HAP; + + // Config + protected deviceLogging!: string; + protected disablePushOn!: boolean; + protected disablePushOff!: boolean; + protected disablePushDetail?: boolean; + + constructor( + protected readonly platform: SwitchBotPlatform, + protected accessory: PlatformAccessory, + protected device: irdevice & irDevicesConfig, + ) { + this.api = this.platform.api; + this.log = this.platform.log; + this.config = this.platform.config; + this.hap = this.api.hap; + + this.getDeviceLogSettings(device); + this.getDeviceConfigSettings(device); + this.getDeviceContext(accessory, device); + this.disablePushOnChanges(device); + this.disablePushOffChanges(device); + + // Set accessory information + accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) + .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(accessory.context.FirmwareRevision); + } + + async getDeviceLogSettings(device: irdevice & irDevicesConfig): Promise { + if (this.platform.debugMode) { + this.deviceLogging = this.accessory.context.logging = 'debugMode'; + this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); + } else if (device.logging) { + this.deviceLogging = this.accessory.context.logging = device.logging; + this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); + } else if (this.config.logging) { + this.deviceLogging = this.accessory.context.logging = this.config.logging; + this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); + } else { + this.deviceLogging = this.accessory.context.logging = 'standard'; + this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); + } + } + + async getDeviceConfigSettings(device: irdevice & irDevicesConfig): Promise { + let config = {}; + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.irair) { + config = device.irair; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.disablePushDetail !== undefined) { + config['disablePushDetail'] = device.disablePushDetail; + } + if (device.irpur) { + config = device.irpur; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.ircam) { + config = device.ircam; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.irfan) { + config = device.irfan; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.irlight) { + config = device.irlight; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.other) { + config = device.other; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.irtv) { + config = device.irtv; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.disablePushDetail !== undefined) { + config['disablePushDetail'] = device.disablePushDetail; + } + if (device.irvc) { + config = device.irvc; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (device.irwh) { + config = device.irwh; + } + if (device.logging !== undefined) { + config['logging'] = device.logging; + } + if (device.connectionType !== undefined) { + config['connectionType'] = device.connectionType; + } + if (device.external !== undefined) { + config['external'] = device.external; + } + if (device.customOn !== undefined) { + config['customOn'] = device.customOn; + } + if (device.customOff !== undefined) { + config['customOff'] = device.customOff; + } + if (device.customize !== undefined) { + config['customize'] = device.customize; + } + if (device.disablePushOn !== undefined) { + config['disablePushOn'] = device.disablePushOn; + } + if (device.disablePushOff !== undefined) { + config['disablePushOff'] = device.disablePushOff; + } + if (Object.entries(config).length !== 0) { + this.infoLog(`Lock: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + } + } + + async getDeviceContext(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): Promise { + accessory.context.name = device.deviceName; + accessory.context.model = device.model; + accessory.context.deviceId = device.deviceId; + accessory.context.remoteType = device.remoteType; + if (device.firmware === undefined) { + device.firmware = this.platform.version; + accessory.context.FirmwareRevision = device.firmware; + } else { + accessory.context.FirmwareRevision = device.firmware; + } + } + + async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { + if (device.disablePushOn === undefined) { + this.disablePushOn = false; + } else { + this.disablePushOn = device.disablePushOn; + } + } + + async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { + if (device.disablePushOff === undefined) { + this.disablePushOff = false; + } else { + this.disablePushOff = device.disablePushOff; + } + } + + async disablePushDetailChanges(device: irdevice & irDevicesConfig): Promise { + if (device.disablePushDetail === undefined) { + this.disablePushDetail = false; + } else { + this.disablePushDetail = device.disablePushDetail; + } + } + + async commandType(): Promise { + let commandType: string; + if (this.device.commandType && this.device.customize) { + commandType = this.device.commandType; + } else if (this.device.customize) { + commandType = 'customize'; + } else { + commandType = 'command'; + } + return commandType; + } + + async commandOn(): Promise { + let command: string; + if (this.device.customize && this.device.customOn) { + command = this.device.customOn; + } else { + command = 'turnOn'; + } + return command; + } + + async commandOff(): Promise { + let command: string; + if (this.device.customize && this.device.customOff) { + command = this.device.customOff; + } else { + command = 'turnOff'; + } + return command; + } + + async statusCode(statusCode: number): Promise { + switch (statusCode) { + case 151: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); + break; + case 152: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); + break; + case 160: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); + break; + case 161: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); + break; + case 171: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + + `Hub: ${this.device.hubDeviceId}`); + break; + case 190: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + + ` server, Or command format is invalid, statusCode: ${statusCode}`); + break; + case 100: + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); + break; + case 200: + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); + break; + case 400: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` + + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); + break; + case 401: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` + + `but the request has not been authenticated, statusCode: ${statusCode}`); + break; + case 403: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` + + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); + break; + case 404: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` + + `statusCode: ${statusCode}`); + break; + case 406: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` + + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); + break; + case 415: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` + + `header that is not supported by the server, statusCode: ${statusCode}`); + break; + case 422: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); + break; + case 429: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` + + `requests allowed for a given time window, statusCode: ${statusCode}`); + break; + case 500: + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` + + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); + break; + default: + this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + } + } + + /** + * Logging for Device + */ + successLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.platform.log.success(String(...log)); + } + } + + infoLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.info(String(...log)); + } + } + + warnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.warn(String(...log)); + } + } + + debugWarnLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.log.warn('[DEBUG]', String(...log)); + } + } + } + + errorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.error(String(...log)); + } + } + + debugErrorLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging?.includes('debug')) { + this.log.error('[DEBUG]', String(...log)); + } + } + } + + debugLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + if (this.deviceLogging === 'debug') { + this.log.info('[DEBUG]', String(...log)); + } else { + this.log.debug(String(...log)); + } + } + } + + enablingDeviceLogging(): boolean { + return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + } +} \ No newline at end of file diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 6dd023d1..900eeddd 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -1,18 +1,19 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * light.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Light { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class Light extends irdeviceBase { // Services lightBulbService?: Service; ProgrammableSwitchServiceOn?: Service; @@ -24,36 +25,20 @@ export class Light { ProgrammableSwitchOutputStateOn?: CharacteristicValue; ProgrammableSwitchEventOff?: CharacteristicValue; ProgrammableSwitchOutputStateOff?: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // Config - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; + super(platform, accessory, device); + // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); - - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + if (this.On === undefined) { + this.On = false; + } else { + this.On = accessory.context.On; + } if (!device.irlight?.stateless) { // get the Light service if it exists, otherwise create a new Light service @@ -64,9 +49,6 @@ export class Light { this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } // handle on / off events using the On characteristic this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); @@ -79,9 +61,6 @@ export class Light { this.ProgrammableSwitchServiceOn.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} On`); - if (!this.ProgrammableSwitchServiceOn.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.ProgrammableSwitchServiceOn.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} On`); - } this.ProgrammableSwitchServiceOn.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ validValueRanges: [0, 0], @@ -105,9 +84,6 @@ export class Light { this.ProgrammableSwitchServiceOff.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Off`); - if (!this.ProgrammableSwitchServiceOff.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.ProgrammableSwitchServiceOff.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Off`); - } this.ProgrammableSwitchServiceOff.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ validValueRanges: [0, 0], @@ -305,128 +281,6 @@ export class Light { } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError(e: any): Promise { if (this.device.irlight?.stateless) { this.lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, e); @@ -437,123 +291,4 @@ export class Light { this.ProgrammableSwitchServiceOff?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); } } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irlight) { - config = device.irlight; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 61cb4b5f..b780f8cc 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -1,67 +1,90 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * other.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Others { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; - // Services - fanService?: Service; - doorService?: Service; - lockService?: Service; - faucetService?: Service; - windowService?: Service; - switchService?: Service; - outletService?: Service; - garageDoorService?: Service; - windowCoveringService?: Service; - statefulProgrammableSwitchService?: Service; - - // Characteristic Values - On?: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; +export class Others extends irdeviceBase { + + private Switch?: { + Service: Service; + On: CharacteristicValue; + }; + + private GarageDoor?: { + Service: Service; + On: CharacteristicValue; + }; + + private Door?: { + Service: Service; + On: CharacteristicValue; + }; + + private Window?: { + Service: Service; + On: CharacteristicValue; + }; + + private WindowCovering?: { + Service: Service; + On: CharacteristicValue; + }; + + private Lock?: { + Service: Service; + On: CharacteristicValue; + }; + + private Faucet?: { + Service: Service; + On: CharacteristicValue; + }; + + private Fan?: { + Service: Service; + On: CharacteristicValue; + }; + + private StatefulProgrammableSwitch?: { + Service: Service; + On: CharacteristicValue; + }; + + private Outlet?: { + Service: Service; + On: CharacteristicValue; + }; // Config - deviceLogging!: string; - disablePushOn?: boolean; otherDeviceType?: string; - disablePushOff?: boolean; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceType(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // default placeholders + this.getOtherConfigSettings(device); // deviceType if (this.otherDeviceType === 'switch') { + // Initialize Switch property + this.Switch = { + Service: accessory.addService(this.hap.Service.Switch), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -72,18 +95,20 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add switchService - const switchService = `${accessory.displayName} Switch`; - (this.switchService = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), switchService; + // Add Switch Service + const SwitchService = `${accessory.displayName} Switch`; + (this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service + || accessory.addService(this.hap.Service.Switch)), SwitchService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Switch`); - this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.switchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.switchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'garagedoor') { + // Initialize Garage Door property + this.GarageDoor = { + Service: accessory.addService(this.hap.Service.GarageDoorOpener), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -94,19 +119,21 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add garageDoorService - const garageDoorService = `${accessory.displayName} Garage Door Opener`; - (this.garageDoorService = accessory.getService(this.hap.Service.GarageDoorOpener) - || accessory.addService(this.hap.Service.GarageDoorOpener)), garageDoorService; + // Add GarageDoor Service + const GarageDoorService = `${accessory.displayName} Garage Door Opener`; + (this.GarageDoor!.Service = accessory.getService(this.hap.Service.GarageDoorOpener) as Service + || accessory.addService(this.hap.Service.GarageDoorOpener)), GarageDoorService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Garage Door Opener`); - this.garageDoorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.garageDoorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.garageDoorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.garageDoorService.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); - this.garageDoorService.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); + this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.GarageDoor!.Service.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); + this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); } else if (this.otherDeviceType === 'door') { + // Initialize Door property + this.Door = { + Service: accessory.addService(this.hap.Service.Door), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeOutletService(accessory); @@ -117,17 +144,14 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add doorService - const doorService = `${accessory.displayName} Door`; - (this.doorService = accessory.getService(this.hap.Service.Door) - || accessory.addService(this.hap.Service.Door)), doorService; + // Add Door Service + const DoorService = `${accessory.displayName} Door`; + (this.Door!.Service = accessory.getService(this.hap.Service.Door) as Service + || accessory.addService(this.hap.Service.Door)), DoorService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Door`); - this.doorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.doorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.doorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.doorService + this.Door!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Door!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -136,8 +160,13 @@ export class Others { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.doorService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Door!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.otherDeviceType === 'window') { + // Initialize Window property + this.Window = { + Service: accessory.addService(this.hap.Service.Window), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -148,17 +177,14 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add windowService - const windowService = `${accessory.displayName} Window`; - (this.windowService = accessory.getService(this.hap.Service.Window) - || accessory.addService(this.hap.Service.Window)), windowService; + // Add Window Service + const WindowService = `${accessory.displayName} Window`; + (this.Window!.Service = accessory.getService(this.hap.Service.Window) as Service + || accessory.addService(this.hap.Service.Window)), WindowService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window`); - this.windowService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.windowService + this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Window!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -167,8 +193,13 @@ export class Others { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.windowService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Window!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.otherDeviceType === 'windowcovering') { + // Initialize WindowCovering property + this.WindowCovering = { + Service: accessory.addService(this.hap.Service.WindowCovering), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -179,17 +210,14 @@ export class Others { this.removeGarageDoorService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add windowCoveringService - const windowCoveringService = `${accessory.displayName} Window Covering`; - (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService; + // Add WindowCovering Service + const WindowCoveringService = `${accessory.displayName} Window Covering`; + (this.WindowCovering!.Service = accessory.getService(this.hap.Service.WindowCovering) as Service + || accessory.addService(this.hap.Service.WindowCovering)), WindowCoveringService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window Covering`); - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.windowCoveringService + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.WindowCovering!.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -198,8 +226,13 @@ export class Others { minStep: 100, }) .onSet(this.OnSet.bind(this)); - this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); } else if (this.otherDeviceType === 'lock') { + // Initialize Lock property + this.Lock = { + Service: accessory.addService(this.hap.Service.LockMechanism), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeDoorService(accessory); this.removeOutletService(accessory); @@ -210,18 +243,20 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add lockService - const lockService = `${accessory.displayName} Lock`; - (this.lockService = accessory.getService(this.hap.Service.LockMechanism) - || accessory.addService(this.hap.Service.LockMechanism)), lockService; + // Add Lock Service + const LockService = `${accessory.displayName} Lock`; + (this.Lock!.Service = accessory.getService(this.hap.Service.LockMechanism) as Service + || accessory.addService(this.hap.Service.LockMechanism)), LockService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Lock`); - this.lockService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.lockService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.lockService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.lockService.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); + this.Lock!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Lock!.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'faucet') { + // Initialize Faucet property + this.Faucet = { + Service: accessory.addService(this.hap.Service.Faucet), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -232,18 +267,20 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add faucetService - const faucetService = `${accessory.displayName} Faucet`; - (this.faucetService = accessory.getService(this.hap.Service.Faucet) - || accessory.addService(this.hap.Service.Faucet)), faucetService; + // Add Faucet Service + const FaucetService = `${accessory.displayName} Faucet`; + (this.Faucet!.Service = accessory.getService(this.hap.Service.Faucet) as Service + || accessory.addService(this.hap.Service.Faucet)), FaucetService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Faucet`); - this.faucetService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.faucetService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.faucetService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.faucetService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); + this.Faucet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Faucet!.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'fan') { + // Initialize Fan property + this.Fan = { + Service: accessory.addService(this.hap.Service.Fanv2), + On: accessory.context.On || true, + }; this.removeLockService(accessory); this.removeDoorService(accessory); this.removeFaucetService(accessory); @@ -254,18 +291,20 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add fanService - const fanService = `${accessory.displayName} Fan`; - (this.fanService = accessory.getService(this.hap.Service.Fan) - || accessory.addService(this.hap.Service.Fan)), fanService; + // Add Fan Service + const FanService = `${accessory.displayName} Fan`; + (this.Fan!.Service = accessory.getService(this.hap.Service.Fanv2) as Service + || accessory.addService(this.hap.Service.Fanv2)), FanService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Fan`); - this.fanService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.fanService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.fanService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.fanService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Fan!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Fan!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'stateful') { + // Initialize StatefulProgrammableSwitch property + this.StatefulProgrammableSwitch = { + Service: accessory.addService(this.hap.Service.StatefulProgrammableSwitch), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -276,20 +315,22 @@ export class Others { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); - // Add statefulProgrammableSwitchService - const statefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`; - (this.statefulProgrammableSwitchService = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), statefulProgrammableSwitchService; + // Add StatefulProgrammableSwitch.Service + const StatefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`; + (this.StatefulProgrammableSwitch!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service + || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), StatefulProgrammableSwitchService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); - this.statefulProgrammableSwitchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.statefulProgrammableSwitchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.statefulProgrammableSwitchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.statefulProgrammableSwitchService + this.StatefulProgrammableSwitch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.StatefulProgrammableSwitch!.Service .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) .onSet(this.OnSet.bind(this)); } else { + // Initialize Outlet property + this.Outlet = { + Service: accessory.addService(this.hap.Service.Outlet), + On: accessory.context.On || true, + }; this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -300,17 +341,14 @@ export class Others { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - // Add outletService - const outletService = `${accessory.displayName} Outlet`; - (this.outletService = accessory.getService(this.hap.Service.Outlet) - || accessory.addService(this.hap.Service.Outlet)), outletService; + // Add Outlet.Service + const OutletService = `${accessory.displayName} Outlet`; + (this.Outlet!.Service = accessory.getService(this.hap.Service.Outlet) as Service + || accessory.addService(this.hap.Service.Outlet)), OutletService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Outlet`); - this.outletService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.outletService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.outletService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } - this.outletService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Outlet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } } @@ -318,55 +356,75 @@ export class Others { * Handle requests to set the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { + let On: boolean; if (this.otherDeviceType === 'garagedoor') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`); if (value === this.hap.Characteristic.TargetDoorState.CLOSED) { - this.On = false; + this.GarageDoor!.On = false; + } else { + this.GarageDoor!.On = true; + } + On = this.GarageDoor!.On; + } else if (this.otherDeviceType === 'door') { + this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + this.Door!.On = false; } else { - this.On = true; + this.Door!.On = true; } - } else if ( - this.otherDeviceType === 'door' || - this.otherDeviceType === 'window' || - this.otherDeviceType === 'windowcovering' - ) { + On = this.Door!.On; + } else if (this.otherDeviceType === 'window') { + this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + this.Window!.On = false; + } else { + this.Window!.On = true; + } + On = this.Window!.On; + } else if (this.otherDeviceType === 'windowcovering') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); if (value === 0) { - this.On = false; + this.WindowCovering!.On = false; } else { - this.On = true; + this.WindowCovering!.On = true; } + On = this.WindowCovering!.On; } else if (this.otherDeviceType === 'lock') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); if (value === this.hap.Characteristic.LockTargetState.SECURED) { - this.On = false; + this.Lock!.On = false; } else { - this.On = true; + this.Lock!.On = true; } + On = this.Lock!.On; } else if (this.otherDeviceType === 'faucet') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set Active: ${value}`); if (value === this.hap.Characteristic.Active.INACTIVE) { - this.On = false; + this.Faucet!.On = false; } else { - this.On = true; + this.Faucet!.On = true; } + On = this.Faucet!.On; } else if (this.otherDeviceType === 'stateful') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); if (value === 0) { - this.On = false; + this.StatefulProgrammableSwitch!.On = false; } else { - this.On = true; + this.StatefulProgrammableSwitch!.On = true; } + + On = this.StatefulProgrammableSwitch!.On; } else { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set On: ${value}`); - this.On = value; + this.Outlet!.On = value; + On = this.Outlet!.On ? true : false; } //pushChanges - if (this.On) { - await this.pushOnChanges(); + if (On === true) { + await this.pushOnChanges(On); } else { - await this.pushOffChanges(); + await this.pushOffChanges(On); } } @@ -380,13 +438,11 @@ export class Others { * Other - "command" "channelAdd" "default" = next channel * Other - "command" "channelSub" "default" = previous channel */ - async pushOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${this.On},` + - ` disablePushOn: ${this.disablePushOn}, customize: ${this.device.customize}, customOn: ${this.device.customOn}`, - ); + async pushOnChanges(On: boolean): Promise { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${On},` + +` disablePushOn: ${this.disablePushOn}, customize: ${this.device.customize}, customOn: ${this.device.customOn}`); if (this.device.customize) { - if (this.On && !this.disablePushOn) { + if (On === true && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -401,13 +457,11 @@ export class Others { } } - async pushOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.On},` + - ` disablePushOff: ${this.disablePushOff}, customize: ${this.device.customize}, customOff: ${this.device.customOff}`, - ); + async pushOffChanges(On: boolean): Promise { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${On},` + + ` disablePushOff: ${this.disablePushOff}, customize: ${this.device.customize}, customOff: ${this.device.customOff}`); if (this.device.customize) { - if (!this.On && !this.disablePushOff) { + if (On === false && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -447,456 +501,294 @@ export class Others { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } async updateHomeKitCharacteristics(): Promise { if (this.otherDeviceType === 'garagedoor') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.GarageDoor!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.GarageDoor!.On}`); } else { - if (this.On) { - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.TargetDoorState, - this.hap.Characteristic.TargetDoorState.OPEN, - ); - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.CurrentDoorState, - this.hap.Characteristic.CurrentDoorState.OPEN, - ); - this.debugLog( - `${this.device.remoteType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`, - ); + if (this.GarageDoor!.On) { + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); } else { - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.TargetDoorState, - this.hap.Characteristic.TargetDoorState.CLOSED, - ); - this.garageDoorService?.updateCharacteristic( - this.hap.Characteristic.CurrentDoorState, - this.hap.Characteristic.CurrentDoorState.CLOSED, - ); - this.debugLog( - `${this.device.remoteType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`, - ); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetDoorState: Closed, CurrentDoorState: Closed (${this.GarageDoor!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Garage Door On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor!.On}`); } else if (this.otherDeviceType === 'door') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Door!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Door!.On}`); } else { - if (this.On) { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.Door!.On) { + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door!.On})`); } else { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Door On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Door On: ${this.Door!.On}`); } else if (this.otherDeviceType === 'window') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Window!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Window!.On}`); } else { - if (this.On) { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.Window!.On) { + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window!.On})`); } else { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window On: ${this.Window!.On}`); } else if (this.otherDeviceType === 'windowcovering') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.WindowCovering!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.WindowCovering!.On}`); } else { - if (this.On) { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.windowCoveringService?.updateCharacteristic( - this.hap.Characteristic.PositionState, - this.hap.Characteristic.PositionState.STOPPED, - ); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + if (this.WindowCovering!.On) { + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering!.On})`); } else { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.windowCoveringService?.updateCharacteristic( - this.hap.Characteristic.PositionState, - this.hap.Characteristic.PositionState.STOPPED, - ); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window Covering On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); } else if (this.otherDeviceType === 'lock') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Lock!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Lock!.On}`); } else { - if (this.On) { - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockTargetState, - this.hap.Characteristic.LockTargetState.UNSECURED, - ); - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockCurrentState, - this.hap.Characteristic.LockCurrentState.UNSECURED, - ); - this.debugLog( - `${this.device.remoteType}: ` + - `${this.accessory.displayName} updateCharacteristic LockTargetState: UNSECURED, LockCurrentState: UNSECURED`, - ); + if (this.Lock!.On) { + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURE (${this.Lock!.On})`); } else { - this.lockService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.lockService?.updateCharacteristic( - this.hap.Characteristic.LockCurrentState, - this.hap.Characteristic.LockCurrentState.SECURED, - ); - this.debugLog( - `${this.device.remoteType}: ` + `${this.accessory.displayName} updateCharacteristic LockTargetState: SECURED, LockCurrentState: SECURED`, - ); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); + this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.Lock!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Lock On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Lock On: ${this.Lock!.On}`); } else if (this.otherDeviceType === 'faucet') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Faucet!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Faucet!.On}`); } else { - if (this.On) { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + if (this.Faucet!.On) { + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); } else { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Faucet On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Faucet On: ${this.Faucet!.On}`); } else if (this.otherDeviceType === 'fan') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Fan!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Fan!.On}`); } else { - if (this.On) { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + if (this.Fan!.On) { + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); } else { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Fan On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Fan On: ${this.Fan!.On}`); } else if (this.otherDeviceType === 'stateful') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.StatefulProgrammableSwitch!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.StatefulProgrammableSwitch!.On}`); } else { - if (this.On) { - this.statefulProgrammableSwitchService?.updateCharacteristic( - this.hap.Characteristic.ProgrammableSwitchEvent, - this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, - ); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); - this.debugLog( - `${this.device.remoteType}: ` + - `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1`, - ); + if (this.StatefulProgrammableSwitch!.On) { + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch!.On})`); } else { - this.statefulProgrammableSwitchService?.updateCharacteristic( - this.hap.Characteristic.ProgrammableSwitchEvent, - this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, - ); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); - this.debugLog( - `${this.device.remoteType}: ` + - `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0`, - ); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch!.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch!.On}`); } else if (this.otherDeviceType === 'switch') { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Switch!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Switch!.On}`); } else { - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch!.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch!.On}`); } } else { - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Outlet!.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Outlet!.On}`); } else { - this.outletService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet!.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet!.On}`); } } } - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.commandType && this.device.customize) { - commandType = this.device.commandType; - } else if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); - } - } - async apiError(e: any): Promise { if (this.otherDeviceType === 'garagedoor') { - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); - this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); + this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); } else if (this.otherDeviceType === 'door') { - this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); } else if (this.otherDeviceType === 'window') { - this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); } else if (this.otherDeviceType === 'windowcovering') { - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.PositionState, e); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); } else if (this.otherDeviceType === 'lock') { - this.doorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); - this.doorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); + this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); } else if (this.otherDeviceType === 'faucet') { - this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, e); + this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, e); } else if (this.otherDeviceType === 'fan') { - this.fanService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, e); } else if (this.otherDeviceType === 'stateful') { - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); } else if (this.otherDeviceType === 'switch') { - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, e); } else { - this.outletService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, e); } } async removeOutletService(accessory: PlatformAccessory): Promise { - // If outletService still present, then remove first - this.outletService = this.accessory.getService(this.hap.Service.Outlet); - if (this.outletService) { + // If Outlet.Service still present, then remove first + this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + if (this.Outlet!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); } - accessory.removeService(this.outletService!); + accessory.removeService(this.Outlet!.Service); } async removeGarageDoorService(accessory: PlatformAccessory): Promise { - // If garageDoorService still present, then remove first - this.garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener); - if (this.garageDoorService) { + // If GarageDoor.Service still present, then remove first + this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + if (this.GarageDoor!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); } - accessory.removeService(this.garageDoorService!); + accessory.removeService(this.GarageDoor!.Service); } async removeDoorService(accessory: PlatformAccessory): Promise { - // If doorService still present, then remove first - this.doorService = this.accessory.getService(this.hap.Service.Door); - if (this.doorService) { + // If Door.Service still present, then remove first + this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + if (this.Door!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); } - accessory.removeService(this.doorService!); + accessory.removeService(this.Door!.Service); } async removeLockService(accessory: PlatformAccessory): Promise { - // If lockService still present, then remove first - this.lockService = this.accessory.getService(this.hap.Service.LockMechanism); - if (this.lockService) { + // If Lock.Service still present, then remove first + this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + if (this.Lock!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); } - accessory.removeService(this.lockService!); + accessory.removeService(this.Lock!.Service); } async removeFaucetService(accessory: PlatformAccessory): Promise { - // If faucetService still present, then remove first - this.faucetService = this.accessory.getService(this.hap.Service.Faucet); - if (this.faucetService) { + // If Faucet.Service still present, then remove first + this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; + if (this.Faucet!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); } - accessory.removeService(this.faucetService!); + accessory.removeService(this.Faucet!.Service); } async removeFanService(accessory: PlatformAccessory): Promise { - // If fanService still present, then remove first - this.fanService = this.accessory.getService(this.hap.Service.Fan); - if (this.fanService) { + // If Fan Service still present, then remove first + this.Fan!.Service = this.accessory.getService(this.hap.Service.Fan) as Service; + if (this.Fan!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); } - accessory.removeService(this.fanService!); + accessory.removeService(this.Fan!.Service); } async removeWindowService(accessory: PlatformAccessory): Promise { - // If windowService still present, then remove first - this.windowService = this.accessory.getService(this.hap.Service.Window); - if (this.windowService) { + // If Window.Service still present, then remove first + this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + if (this.Window!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); } - accessory.removeService(this.windowService!); + accessory.removeService(this.Window!.Service); } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { - // If windowCoveringService still present, then remove first - this.windowCoveringService = this.accessory.getService(this.hap.Service.WindowCovering); - if (this.windowCoveringService) { + // If WindowCovering.Service still present, then remove first + this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + if (this.WindowCovering!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); } - accessory.removeService(this.windowCoveringService!); + accessory.removeService(this.WindowCovering!.Service); } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { - // If statefulProgrammableSwitchService still present, then remove first - this.statefulProgrammableSwitchService = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch); - if (this.statefulProgrammableSwitchService) { + // If StatefulProgrammableSwitch.Service still present, then remove first + this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; + if (this.StatefulProgrammableSwitch!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); } - accessory.removeService(this.statefulProgrammableSwitchService!); + accessory.removeService(this.StatefulProgrammableSwitch!.Service); } async removeSwitchService(accessory: PlatformAccessory): Promise { - // If switchService still present, then remove first - this.switchService = this.accessory.getService(this.hap.Service.Switch); - if (this.switchService) { + // If Switch.Service still present, then remove first + this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + if (this.Switch!.Service) { this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); } - accessory.removeService(this.switchService!); + accessory.removeService(this.Switch!.Service); } - async deviceType(device: irdevice & irDevicesConfig): Promise { + async getOtherConfigSettings(device: irdevice & irDevicesConfig): Promise { if (!device.other?.deviceType && this.accessory.context.deviceType) { this.otherDeviceType = this.accessory.context.deviceType; this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Type: ${this.otherDeviceType}, from Accessory Cache.`); @@ -910,123 +802,4 @@ export class Others { this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName} no deviceType set, using default deviceType: ${this.otherDeviceType}`); } } - - async deviceContext() { - if (this.On === undefined) { - this.On = true; - } else { - this.On = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.other) { - config = device.other; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; - } } diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 7c16f2e7..bb87a608 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -1,128 +1,139 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * tv.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class TV { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class TV extends irdeviceBase { // Services - tvService!: Service; - speakerService: Service; + private Television: { + Service: Service; + Active: CharacteristicValue; + ActiveIdentifier: CharacteristicValue; + SleepDiscoveryMode: CharacteristicValue; + RemoteKey: CharacteristicValue; + }; + + private TelevisionSpeaker: { + Service: Service; + Active: CharacteristicValue; + VolumeControlType: CharacteristicValue; + VolumeSelector: CharacteristicValue; + }; // Characteristic Values - Active!: CharacteristicValue; - ActiveIdentifier!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // Config - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; - disablePushDetail?: boolean; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; + super(platform, accessory, device); + + // Initialize Television property + this.Television = { + Service: accessory.addService(this.hap.Service.Television), + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + ActiveIdentifier: accessory.context.ActiveIdentifier || 1, + SleepDiscoveryMode: accessory.context.SleepDiscoveryMode || this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, + RemoteKey: accessory.context.RemoteKey || this.hap.Characteristic.RemoteKey.EXIT, + }; + + // Initialize TelevisionSpeaker property + this.TelevisionSpeaker = { + Service: accessory.addService(this.hap.Service.TelevisionSpeaker), + Active: accessory.context.Active || false, + VolumeControlType: accessory.context.VolumeControlType || this.hap.Characteristic.VolumeControlType.ABSOLUTE, + VolumeSelector: accessory.context.VolumeSelector || this.hap.Characteristic.VolumeSelector.INCREMENT, + }; + // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.disablePushDetailChanges(device); - this.deviceConfig(device); - - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Name, `${device.deviceName} ${device.remoteType}`) - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + if (this.Television.Active === undefined) { + this.Television.Active = this.hap.Characteristic.Active.INACTIVE; + } else { + this.Television.Active = this.accessory.context.Active; + } + if (this.Television.ActiveIdentifier === undefined) { + this.Television.ActiveIdentifier = 1; + } else { + this.Television.ActiveIdentifier = this.accessory.context.ActiveIdentifier; + } // set the accessory category - const tvServiceCategory = `${accessory.displayName} ${device.remoteType}`; + const TelevisionServiceCategory = `${accessory.displayName} ${device.remoteType}`; switch (device.remoteType) { case 'Speaker': case 'DIY Speaker': accessory.category = this.platform.api.hap.Categories.SPEAKER; - (this.tvService = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), tvServiceCategory; + (this.Television.Service = accessory.getService(this.hap.Service.Television) + || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; case 'IPTV': case 'DIY IPTV': accessory.category = this.platform.api.hap.Categories.TV_STREAMING_STICK; - (this.tvService = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), tvServiceCategory; + (this.Television.Service = accessory.getService(this.hap.Service.Television) + || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; case 'DVD': case 'DIY DVD': case 'Set Top Box': case 'DIY Set Top Box': accessory.category = this.platform.api.hap.Categories.TV_SET_TOP_BOX; - (this.tvService = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), tvServiceCategory; + (this.Television.Service = accessory.getService(this.hap.Service.Television) + || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; default: accessory.category = this.platform.api.hap.Categories.TELEVISION; // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - (this.tvService = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), tvServiceCategory; + (this.Television.Service = accessory.getService(this.hap.Service.Television) + || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; } - this.tvService.getCharacteristic(this.hap.Characteristic.ConfiguredName); + this.Television.Service.getCharacteristic(this.hap.Characteristic.ConfiguredName); // set sleep discovery characteristic - this.tvService.setCharacteristic( + this.Television.Service.setCharacteristic( this.hap.Characteristic.SleepDiscoveryMode, this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, ); // handle on / off events using the Active characteristic - this.tvService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.Television.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - this.tvService.setCharacteristic(this.hap.Characteristic.ActiveIdentifier, 1); + this.Television.Service.setCharacteristic(this.hap.Characteristic.ActiveIdentifier, 1); // handle input source changes - this.tvService.getCharacteristic(this.hap.Characteristic.ActiveIdentifier).onSet(this.ActiveIdentifierSet.bind(this)); + this.Television.Service.getCharacteristic(this.hap.Characteristic.ActiveIdentifier).onSet(this.ActiveIdentifierSet.bind(this)); // handle remote control input - this.tvService.getCharacteristic(this.hap.Characteristic.RemoteKey).onSet(this.RemoteKeySet.bind(this)); + this.Television.Service.getCharacteristic(this.hap.Characteristic.RemoteKey).onSet(this.RemoteKeySet.bind(this)); /** * Create a speaker service to allow volume control */ // create a new Television Speaker service - const speakerService = `${accessory.displayName} Speaker`; - (this.speakerService = accessory.getService(this.hap.Service.TelevisionSpeaker) - || accessory.addService(this.hap.Service.TelevisionSpeaker)), speakerService; + const TelevisionSpeakerService = `${accessory.displayName} Speaker`; + (this.TelevisionSpeaker.Service = accessory.getService(this.hap.Service.TelevisionSpeaker) + || accessory.addService(this.hap.Service.TelevisionSpeaker)), TelevisionSpeakerService; - this.speakerService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Speaker`); - if (!this.speakerService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.speakerService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Speaker`); - } - this.speakerService + this.TelevisionSpeaker.Service.setCharacteristic(this.hap.Characteristic.Name, TelevisionSpeakerService); + this.TelevisionSpeaker.Service .setCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE) .setCharacteristic(this.hap.Characteristic.VolumeControlType, this.hap.Characteristic.VolumeControlType.ABSOLUTE); // handle volume control - this.speakerService.getCharacteristic(this.hap.Characteristic.VolumeSelector).onSet(this.VolumeSelectorSet.bind(this)); + this.TelevisionSpeaker.Service.getCharacteristic(this.hap.Characteristic.VolumeSelector).onSet(this.VolumeSelectorSet.bind(this)); } async VolumeSelectorSet(value: CharacteristicValue): Promise { @@ -200,14 +211,14 @@ export class TV { async ActiveIdentifierSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${value}`); - this.ActiveIdentifier = value; + this.Television.ActiveIdentifier = value; } async ActiveSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active (value): ${value}`); - this.Active = value; - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + this.Television.Active = value; + if (this.Television.Active === this.hap.Characteristic.Active.ACTIVE) { await this.pushTvOnChanges(); } else { await this.pushTvOffChanges(); @@ -225,10 +236,9 @@ export class TV { * TV "command" "channelSub" "default" previous channel */ async pushTvOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushTvOnChanges Active: ${this.Active},` + ` disablePushOn: ${this.disablePushOn}`, - ); - if (this.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushTvOnChanges` + + ` Active: ${this.Television.Active}, disablePushOn: ${this.disablePushOn}`); + if (this.Television.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -241,10 +251,9 @@ export class TV { } async pushTvOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushTvOffChanges Active: ${this.Active},` + ` disablePushOff: ${this.disablePushOff}`, - ); - if (this.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushTvOffChanges` + + ` Active: ${this.Television.Active}, disablePushOff: ${this.disablePushOff}`); + if (this.Television.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -404,282 +413,26 @@ export class TV { async updateHomeKitCharacteristics(): Promise { // Active - if (this.Active === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); + if (this.Television.Active === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Television.Active}`); } else { - this.accessory.context.Active = this.Active; - this.tvService?.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + this.accessory.context.Active = this.Television.Active; + this.Television.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Television.Active); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Television.Active}`); } // ActiveIdentifier - if (this.ActiveIdentifier === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${this.ActiveIdentifier}`); - } else { - this.accessory.context.ActiveIdentifier = this.ActiveIdentifier; - this.tvService?.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, this.ActiveIdentifier); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic ActiveIdentifier: ${this.ActiveIdentifier}`); - } - } - - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async disablePushDetailChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushDetail === undefined) { - this.disablePushDetail = false; - } else { - this.disablePushDetail = device.disablePushDetail; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; + if (this.Television.ActiveIdentifier === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${this.Television.ActiveIdentifier}`); } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; - } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + this.accessory.context.ActiveIdentifier = this.Television.ActiveIdentifier; + this.Television.Service?.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, this.Television.ActiveIdentifier); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` ActiveIdentifier: ${this.Television.ActiveIdentifier}`); } } async apiError(e: any): Promise { - this.tvService.updateCharacteristic(this.hap.Characteristic.Active, e); - this.tvService.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, e); - } - - async deviceContext() { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.ActiveIdentifier === undefined) { - this.ActiveIdentifier = 1; - } else { - this.ActiveIdentifier = this.accessory.context.ActiveIdentifier; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irtv) { - config = device.irtv; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (device.disablePushDetail !== undefined) { - config['disablePushDetail'] = device.disablePushDetail; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Television.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.Television.Service.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, e); } } diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 64e6c30c..bf9e1345 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -1,74 +1,55 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * vacuumcleaner.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class VacuumCleaner { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class VacuumCleaner extends irdeviceBase { // Services - switchService!: Service; - - // Characteristic Values - On!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // Config - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; + private Switch: { + Service: Service; + On: CharacteristicValue; + }; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Switch property + this.Switch = { + Service: accessory.addService(this.hap.Service.Switch), + On: accessory.context.On || false, + }; - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const switchService = `${accessory.displayName} ${device.remoteType}`; - (this.switchService = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), switchService; + // get the Switch service if it exists, otherwise create a new Switch service + const SwitchService = `${accessory.displayName} ${device.remoteType}`; + (this.Switch.Service = accessory.getService(this.hap.Service.Switch) + || accessory.addService(this.hap.Service.Switch)), SwitchService; - this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.switchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.switchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.Switch.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the On characteristic - this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } async OnSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`); - this.On = value; - if (this.On) { + // Set the requested state + this.Switch.On = value; + if (this.Switch.On) { await this.pushOnChanges(); } else { await this.pushOffChanges(); @@ -82,8 +63,9 @@ export class VacuumCleaner { * Vacuum Cleaner "command" "turnOn" "default" set to ON state */ async pushOnChanges(): Promise { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${this.On},` + ` disablePushOn: ${this.disablePushOn}`); - if (this.On && !this.disablePushOn) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges` + + ` On: ${this.Switch.On}, disablePushOn: ${this.disablePushOn}`); + if (this.Switch.On && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -96,10 +78,9 @@ export class VacuumCleaner { } async pushOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.On},` + ` disablePushOff: ${this.disablePushOff}`, - ); - if (!this.On && !this.disablePushOff) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges` + + ` On: ${this.Switch.On}, disablePushOff: ${this.disablePushOff}`); + if (!this.Switch.On && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -136,272 +117,27 @@ export class VacuumCleaner { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChange` + + ` with ${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); - } else { - this.accessory.context.On = this.On; - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); - } - } - - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; + if (this.Switch.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Switch.On}`); } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + this.accessory.context.On = this.Switch.On; + this.Switch.Service?.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`); } } async apiError(e: any): Promise { - this.switchService.updateCharacteristic(this.hap.Characteristic.On, e); - } - - async deviceContext() { - if (this.On === undefined) { - this.On = false; - } else { - this.On = this.accessory.context.On; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irvc) { - config = device.irvc; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, e); } } diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index 9a262951..f10d73d2 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -1,82 +1,63 @@ -import { CharacteristicValue, PlatformAccessory, Service, API, Logging, HAP } from 'homebridge'; +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * waterheater.ts: @switchbot/homebridge-switchbot. + */ import { request } from 'undici'; +import { irdeviceBase } from './irdevice.js'; import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice, SwitchBotPlatformConfig } from '../settings.js'; +import { Devices, irDevicesConfig, irdevice } from '../settings.js'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class WaterHeater { - public readonly api: API; - public readonly log: Logging; - public readonly config!: SwitchBotPlatformConfig; - protected readonly hap: HAP; +export class WaterHeater extends irdeviceBase { // Services - valveService!: Service; - - // Characteristic Values - Active!: CharacteristicValue; - FirmwareRevision!: CharacteristicValue; - - // Config - deviceLogging!: string; - disablePushOn?: boolean; - disablePushOff?: boolean; + private Valve: { + Service: Service; + Active: CharacteristicValue; + }; constructor( - private readonly platform: SwitchBotPlatform, - private accessory: PlatformAccessory, - public device: irdevice & irDevicesConfig, + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: irdevice & irDevicesConfig, ) { - this.api = this.platform.api; - this.log = this.platform.log; - this.config = this.platform.config; - this.hap = this.api.hap; - // default placeholders - this.deviceLogs(device); - this.deviceContext(); - this.disablePushOnChanges(device); - this.disablePushOffChanges(device); - this.deviceConfig(device); + super(platform, accessory, device); - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, device.remoteType) - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Initialize Valve property + this.Valve = { + Service: accessory.addService(this.hap.Service.Valve), + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + }; // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - const valveService = `${accessory.displayName} ${device.remoteType}`; - (this.valveService = accessory.getService(this.hap.Service.Valve) - || accessory.addService(this.hap.Service.Valve)), valveService; + const ValveService = `${accessory.displayName} ${device.remoteType}`; + (this.Valve.Service = accessory.getService(this.hap.Service.Valve) + || accessory.addService(this.hap.Service.Valve)), ValveService; - this.valveService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - if (!this.valveService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) { - this.valveService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName); - } + this.Valve.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // set sleep discovery characteristic - this.valveService.setCharacteristic(this.hap.Characteristic.ValveType, this.hap.Characteristic.ValveType.GENERIC_VALVE); + this.Valve.Service.setCharacteristic(this.hap.Characteristic.ValveType, this.hap.Characteristic.ValveType.GENERIC_VALVE); // handle on / off events using the Active characteristic - this.valveService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.Valve.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); } async ActiveSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${value}`); - this.Active = value; - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + this.Valve.Active = value; + if (this.Valve.Active === this.hap.Characteristic.Active.ACTIVE) { await this.pushWaterHeaterOnChanges(); - this.valveService.setCharacteristic(this.hap.Characteristic.InUse, this.hap.Characteristic.InUse.IN_USE); + this.Valve.Service.setCharacteristic(this.hap.Characteristic.InUse, this.hap.Characteristic.InUse.IN_USE); } else { await this.pushWaterHeaterOffChanges(); - this.valveService.setCharacteristic(this.hap.Characteristic.InUse, this.hap.Characteristic.InUse.NOT_IN_USE); + this.Valve.Service.setCharacteristic(this.hap.Characteristic.InUse, this.hap.Characteristic.InUse.NOT_IN_USE); } } @@ -87,11 +68,9 @@ export class WaterHeater { * WaterHeater "command" "turnOn" "default" set to ON state */ async pushWaterHeaterOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOnChanges Active: ${this.Active},` + - ` disablePushOn: ${this.disablePushOn}`, - ); - if (this.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOnChanges Active: ${this.Valve.Active},` + + ` disablePushOn: ${this.disablePushOn}`); + if (this.Valve.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -104,11 +83,9 @@ export class WaterHeater { } async pushWaterHeaterOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOffChanges Active: ${this.Active},` + - ` disablePushOff: ${this.disablePushOff}`, - ); - if (this.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOffChanges Active: ${this.Valve.Active},` + + ` disablePushOff: ${this.disablePushOff}`); + if (this.Valve.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -145,272 +122,27 @@ export class WaterHeater { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } async updateHomeKitCharacteristics(): Promise { // Active - if (this.Active === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); - } else { - this.accessory.context.Active = this.Active; - this.valveService?.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); - } - } - - async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOn === undefined) { - this.disablePushOn = false; - } else { - this.disablePushOn = device.disablePushOn; - } - } - - async disablePushOffChanges(device: irdevice & irDevicesConfig): Promise { - if (device.disablePushOff === undefined) { - this.disablePushOff = false; - } else { - this.disablePushOff = device.disablePushOff; - } - } - - async commandType(): Promise { - let commandType: string; - if (this.device.customize) { - commandType = 'customize'; - } else { - commandType = 'command'; - } - return commandType; - } - - async commandOn(): Promise { - let command: string; - if (this.device.customize && this.device.customOn) { - command = this.device.customOn; - } else { - command = 'turnOn'; - } - return command; - } - - async commandOff(): Promise { - let command: string; - if (this.device.customize && this.device.customOff) { - command = this.device.customOff; + if (this.Valve.Active === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Valve.Active}`); } else { - command = 'turnOff'; - } - return command; - } - - async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 151: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); - break; - case 152: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`); - break; - case 160: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`); - break; - case 161: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); - break; - case 171: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`, - ); - break; - case 190: - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + - ` Or command format is invalid, statusCode: ${statusCode}`, - ); - break; - case 100: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); - break; - case 200: - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`); - break; - case 400: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. ` - + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`); - break; - case 401: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, ` - + `but the request has not been authenticated, statusCode: ${statusCode}`); - break; - case 403: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not ` - + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`); - break; - case 404: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, ` - + `statusCode: ${statusCode}`); - break; - case 406: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via ` - + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`); - break; - case 415: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType ` - + `header that is not supported by the server, statusCode: ${statusCode}`); - break; - case 422: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Unprocessable Entity, 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, statusCode: ${statusCode}`); - break; - case 429: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of ` - + `requests allowed for a given time window, statusCode: ${statusCode}`); - break; - case 500: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings ` - + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`); - break; - default: - this.infoLog( - `${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` + - `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`, - ); + this.accessory.context.Active = this.Valve.Active; + this.Valve.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Valve.Active); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Valve.Active}`); } } async apiError({ e }: { e: any }): Promise { - this.valveService.updateCharacteristic(this.hap.Characteristic.Active, e); - } - - async deviceContext(): Promise { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.FirmwareRevision === undefined) { - this.FirmwareRevision = this.platform.version; - this.accessory.context.FirmwareRevision = this.FirmwareRevision; - } - } - - async deviceConfig(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.irwh) { - config = device.irwh; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (Object.entries(config).length !== 0) { - this.debugWarnLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`] }); - } - } - - async deviceLogs(device: irdevice & irDevicesConfig): Promise { - if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); - } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); - } else if (this.platform.config.options?.logging) { - this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); - } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); - } - } - - /** - * Logging for Device - */ - successLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); - } - } - - infoLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.info(String(...log)); - } - } - - warnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.warn(String(...log)); - } - } - - debugWarnLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.warn('[DEBUG]', String(...log)); - } - } - } - - errorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - this.platform.log.error(String(...log)); - } - } - - debugErrorLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging?.includes('debug')) { - this.platform.log.error('[DEBUG]', String(...log)); - } - } - } - - debugLog(...log: any[]): void { - if (this.enablingDeviceLogging()) { - if (this.deviceLogging === 'debug') { - this.platform.log.info('[DEBUG]', String(...log)); - } else { - this.platform.log.debug(String(...log)); - } - } - } - - enablingDeviceLogging(): boolean { - return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard'; + this.Valve.Service.updateCharacteristic(this.hap.Characteristic.Active, e); } } diff --git a/src/settings.ts b/src/settings.ts index 83fc29a3..75873809 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -53,6 +53,7 @@ export interface devicesConfig extends device { deviceId: string; external?: boolean; refreshRate?: number; + updateRate?: number; firmware?: string; logging?: string; connectionType?: string; @@ -71,6 +72,7 @@ export interface devicesConfig extends device { webhook?: boolean; bot?: bot; meter?: meter; + iosensor?: iosensor; humidifier?: humidifier; curtain?: curtain; blindTilt?: blindTilt; @@ -90,6 +92,11 @@ export type meter = { hide_humidity?: boolean; }; +export type iosensor = { + hide_temperature?: boolean; + hide_humidity?: boolean; +}; + export type bot = { mode?: string; deviceType?: string; @@ -109,7 +116,6 @@ export type curtain = { hide_lightsensor?: boolean; set_minLux?: number; set_maxLux?: number; - updateRate?: number; set_max?: number; set_min?: number; set_minStep?: number; @@ -122,7 +128,6 @@ export type blindTilt = { hide_lightsensor?: boolean; set_minLux?: number; set_maxLux?: number; - updateRate?: number; set_max?: number; set_min?: number; set_minStep?: number; @@ -268,6 +273,10 @@ export type device = { version?: number; //BLE Mac Address bleMac?: string; + //Device Model + model?: string; + //Device BLE Model + bleModel?: string; }; //a list of virtual infrared remote devices. @@ -280,9 +289,16 @@ export type irdevice = { deviceName: string; //device name remoteType: string; //device type hubDeviceId: string; //remote device's parent Hub ID + model: string; //device model }; export type deviceStatus = { + statusCode: number; + message: string; + body: deviceStatusBody; +}; + +export type deviceStatusBody = { //v1.1 of API deviceId: string; //device ID. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) deviceType: string; //device type. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) @@ -323,6 +339,7 @@ export type deviceStatus = { shakeCenter?: string; //the fan's swing direction. (Used by the following deviceTypes: Smart Fan) shakeRange?: string; //the fan's swing range, 0~120°. (Used by the following deviceTypes: Smart Fan) status?: number //the leak status. 0 for no leak, 1 for leak. (Used by the following deviceTypes: Water Detector) + lightLevel?: number; //the light level. (Used by the following deviceTypes: Hub) }; export type ad = { @@ -359,7 +376,7 @@ export type serviceData = { battery?: number; //Humidifier's humidity level percentage percentage?: boolean | string; - //Humidifier's humidity level percentage + //Humidifier's state onState?: boolean; //Humidifier's AutoMode autoMode?: boolean; diff --git a/src/utils.ts b/src/utils.ts index 597a06c9..5f570481 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,77 @@ * * util.ts: @switchbot/homebridge-switchbot platform class. */ +export enum SwitchBotModel { + HubMini = 'W0202200', + HubPlus = 'SwitchBot Hub S1', + Hub2 = 'W3202100', + Bot = 'SwitchBot S1', + Curtain = 'W0701600', + Curtain3 = 'W2400000', + Humidifier = 'W0801800', + Plug = 'SP11', // Currently only available in Japan + Meter = 'SwitchBot MeterTH S1', + MeterPlusJP = 'W2201500', + MeterPlusUS = 'W2301500', + OutdoorMeter = 'W3400010', + MotionSensor = 'W1101500', + ContactSensor = 'W1201500', + ColorBulb = 'W1401400', + StripLight = 'W1701100', + PlugMiniUS = 'W1901400', + PlugMiniJP = 'W2001400', + Lock = 'W1601700', + LockPro = 'W3500000', + Keypad = 'W2500010', + KeypadTouch = 'W2500020', + K10 = 'K10+', + WoSweeper = 'WoSweeper', + WoSweeperMini = 'WoSweeperMini', + RobotVacuumCleanerS1 = 'W3011000', // Currently only available in Japan. + RobotVacuumCleanerS1Plus = 'W3011010', // Currently only available in Japan. + Remote = 'Remote', + UniversalRemote = 'UniversalRemote', + CeilingLight = 'W2612230', // Currently only available in Japan. + CeilingLightPro = 'W2612210', // Currently only available in Japan. + IndoorCam = 'W1301200', + PanTiltCam = 'W1801200', + PanTiltCam2K = 'W3101100', + BlindTilt = 'W2701600', + BatteryCirculatorFan = 'W3800510', + WaterDetector = 'WoWaterDetector', + Unknown = 'Unknown', +} + +export enum SwitchBotBLEModel { + Bot = 'H', + Curtain = 'c', + Curtain3 = '{', + Humidifier = 'e', + Meter = 'T', + MeterPlus = 'i', + OutdoorMeter = 'w', + MotionSensor = 's', + ContactSensor = 'd', + ColorBulb = 'u', + StripLight = 'r', + PlugMiniUS = 'g', + PlugMiniJP = 'j', + Lock = 'o', + Remote = '', + CeilingLight = 'q', // Currently only available in Japan. + CeilingLightPro = 'n', // Currently only available in Japan. + BlindTilt = 'x', + Unknown = 'Unknown', +} + +export enum BlindTiltMappingMode { + OnlyUp = 'only_up', + OnlyDown = 'only_down', + DownAndUp = 'down_and_up', + UpAndDown = 'up_and_down', + UseTiltForDirection = 'use_tilt_for_direction', +} + export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } From c415ccbe096b53338557869a43ac6d0e4954261f Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 14 May 2024 15:52:25 -0500 Subject: [PATCH 27/73] revert hb-lib --- package-lock.json | 65 +++++++++++++---------------------------------- package.json | 6 ++--- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index fcb34257..8951c62a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "7.0.1", + "homebridge-lib": "6.7.5", "rxjs": "^7.8.1", "undici": "^6.16.1" }, @@ -40,8 +40,8 @@ "typescript": "^5.4.5" }, "engines": { - "homebridge": "^1.8.0", - "node": "^18 || ^20" + "homebridge": "^1.8.1", + "node": "^18 || ^20 || ^22" }, "optionalDependencies": { "node-switchbot": "2.1.0" @@ -2525,7 +2525,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3052,7 +3051,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3234,7 +3232,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3245,8 +3242,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-support": { "version": "1.1.3", @@ -5093,7 +5089,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5175,13 +5170,13 @@ } }, "node_modules/hb-lib-tools": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.1.tgz", - "integrity": "sha512-okOzIvlR4QgfyCh7qIOczRxJDuv3TdzvWTMFlYK8c4UTSXHnIb/dWBvkud7tQyXa9n8FA2DBTJdAUSWJLM63sA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", + "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", "dependencies": { - "bonjour-hap": "^3.7.2", - "chalk": "^5.3.0", - "semver": "^7.6.2" + "bonjour-hap": "^3.7.1", + "chalk": "^4.1.2", + "semver": "^7.6.0" }, "bin": { "hap": "cli/hap.js", @@ -5190,29 +5185,7 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.13.1||^20||^18" - } - }, - "node_modules/hb-lib-tools/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/hb-lib-tools/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node": "20.12.0||^20||^18" } }, "node_modules/helmet": { @@ -5372,12 +5345,12 @@ } }, "node_modules/homebridge-lib": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.1.tgz", - "integrity": "sha512-OMwQEx1Uhb67VsFoY+s9KXUjc6g7XQP86GrNEvPD7ldnaA/D0HN3KHfsaJ0JyZaNCvUOEqG8A4youCMj73RSSw==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", + "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.3", - "hb-lib-tools": "~2.0.1" + "@homebridge/plugin-ui-utils": "~1.0.1", + "hb-lib-tools": "~1.2.4" }, "bin": { "hap": "cli/hap.js", @@ -5386,8 +5359,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.8.1", - "node": "20.13.1||^20||^18" + "homebridge": "^1.7.0", + "node": "20.12.0||^20||^18" } }, "node_modules/hosted-git-info": { @@ -8970,7 +8943,6 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9715,7 +9687,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index 7530f5b3..22fb6549 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ }, "engineStrict": true, "engines": { - "homebridge": "^1.8.0", - "node": "^18 || ^20" + "homebridge": "^1.8.1", + "node": "^18 || ^20 || ^22" }, "main": "dist/index.js", "scripts": { @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "7.0.1", + "homebridge-lib": "6.7.5", "rxjs": "^7.8.1", "undici": "^6.16.1" }, From 7b40bcfa7da1bf2a4529af76b1e42b60213008ef Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Tue, 14 May 2024 16:20:09 -0500 Subject: [PATCH 28/73] upgrade hb-lib --- package-lock.json | 61 ++++++++++++++++++++++++++++++++++------------- package.json | 2 +- src/platform.ts | 3 +-- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8951c62a..36d25685 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "6.7.5", + "homebridge-lib": "7.0.1", "rxjs": "^7.8.1", "undici": "^6.16.1" }, @@ -2525,6 +2525,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3051,6 +3052,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3232,6 +3234,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3242,7 +3245,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5089,6 +5093,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5170,13 +5175,13 @@ } }, "node_modules/hb-lib-tools": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.4.tgz", - "integrity": "sha512-kdyzYvAbG3c4WPISjeE2VPzGWR8sClIf3u5+qWh727oMHPNrC4ES/hI6r4HXAsokOIG3cgCTpopvElaszLRVDA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.1.tgz", + "integrity": "sha512-okOzIvlR4QgfyCh7qIOczRxJDuv3TdzvWTMFlYK8c4UTSXHnIb/dWBvkud7tQyXa9n8FA2DBTJdAUSWJLM63sA==", "dependencies": { - "bonjour-hap": "^3.7.1", - "chalk": "^4.1.2", - "semver": "^7.6.0" + "bonjour-hap": "^3.7.2", + "chalk": "^5.3.0", + "semver": "^7.6.2" }, "bin": { "hap": "cli/hap.js", @@ -5185,7 +5190,29 @@ "upnp": "cli/upnp.js" }, "engines": { - "node": "20.12.0||^20||^18" + "node": "20.13.1||^20||^18" + } + }, + "node_modules/hb-lib-tools/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/hb-lib-tools/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/helmet": { @@ -5345,12 +5372,12 @@ } }, "node_modules/homebridge-lib": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.5.tgz", - "integrity": "sha512-B+UGFcDa8Kw84bIGoEIjuft+vI6HgqaOMD78iJVx4p6QG+j0xgp9fA/X50Zc0oEqILLUnRQ4L2xELRoiAs/Hkg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.1.tgz", + "integrity": "sha512-OMwQEx1Uhb67VsFoY+s9KXUjc6g7XQP86GrNEvPD7ldnaA/D0HN3KHfsaJ0JyZaNCvUOEqG8A4youCMj73RSSw==", "dependencies": { - "@homebridge/plugin-ui-utils": "~1.0.1", - "hb-lib-tools": "~1.2.4" + "@homebridge/plugin-ui-utils": "~1.0.3", + "hb-lib-tools": "~2.0.1" }, "bin": { "hap": "cli/hap.js", @@ -5359,8 +5386,8 @@ "upnp": "cli/upnp.js" }, "engines": { - "homebridge": "^1.7.0", - "node": "20.12.0||^20||^18" + "homebridge": "^1.8.1", + "node": "20.13.1||^20||^18" } }, "node_modules/hosted-git-info": { @@ -8943,6 +8970,7 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9687,6 +9715,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index 22fb6549..c40e1eb5 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "6.7.5", + "homebridge-lib": "7.0.1", "rxjs": "^7.8.1", "undici": "^6.16.1" }, diff --git a/src/platform.ts b/src/platform.ts index 22cd4fba..fe8ddb51 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -39,7 +39,7 @@ import fakegato from 'fakegato-history'; import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; -import hbLib from 'homebridge-lib'; +import { EveHomeKitTypes } from 'homebridge-lib'; import { UrlObject } from 'url'; import { sleep } from './utils.js'; @@ -108,7 +108,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } // import fakegato-history module and EVE characteristics - const { EveHomeKitTypes } = hbLib; this.fakegatoAPI = fakegato(api); this.eve = new EveHomeKitTypes(api); From 22588682f287d7395425b869327ffe123f524c2e Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 15 May 2024 13:15:16 -0500 Subject: [PATCH 29/73] more fixes --- package-lock.json | 2 +- package.json | 2 +- src/custom.d.ts | 1 + src/device/blindtilt.ts | 38 +-- src/device/bot.ts | 28 +- src/device/ceilinglight.ts | 8 +- src/device/colorbulb.ts | 8 +- src/device/contact.ts | 12 +- src/device/curtain.ts | 27 +- src/device/hub.ts | 13 +- src/device/humidifier.ts | 10 +- src/device/iosensor.ts | 12 +- src/device/lightstrip.ts | 8 +- src/device/lock.ts | 14 +- src/device/meter.ts | 12 +- src/device/meterplus.ts | 12 +- src/device/motion.ts | 12 +- src/device/plug.ts | 8 +- src/device/robotvacuumcleaner.ts | 10 +- src/device/waterdetector.ts | 26 +- src/irdevice/airconditioner.ts | 477 ++++++++++++++++--------------- src/irdevice/airpurifier.ts | 131 +++++---- src/irdevice/camera.ts | 54 ++-- src/irdevice/fan.ts | 126 ++++---- src/irdevice/irdevice.ts | 2 +- src/irdevice/light.ts | 32 ++- src/irdevice/other.ts | 102 +++---- src/irdevice/tv.ts | 4 +- src/irdevice/vacuumcleaner.ts | 2 +- src/irdevice/waterheater.ts | 2 +- src/platform.ts | 25 +- 31 files changed, 628 insertions(+), 592 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36d25685..bd48f8f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "7.0.1", + "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", "undici": "^6.16.1" }, diff --git a/package.json b/package.json index c40e1eb5..3300b103 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@homebridge/plugin-ui-utils": "^1.0.3", "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.4", - "homebridge-lib": "7.0.1", + "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", "undici": "^6.16.1" }, diff --git a/src/custom.d.ts b/src/custom.d.ts index ebb1e5af..49926c56 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -3,4 +3,5 @@ * custom.d.ts: @switchbot/homebridge-switchbot platform class. */ declare module 'homebridge-lib' +declare module 'homebridge-lib/EveHomeKitTypes' declare module 'fakegato-history' diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index f4f99cc3..fcf7913a 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -60,34 +60,34 @@ export class BlindTilt extends deviceBase { this.blindTiltUpdateInProgress = false; this.setNewTarget = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize LightBulb property this.WindowCovering = { - Service: this.accessory.addService(this.hap.Service.WindowCovering), - PositionState: this.accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, - TargetPosition: this.accessory.context.TargetPosition || 100, - CurrentPosition: this.accessory.context.CurrentPosition || 100, - TargetHorizontalTiltAngle: this.accessory.context.TargetHorizontalTiltAngle || 0, - CurrentHorizontalTiltAngle: this.accessory.context.CurrentHorizontalTiltAngle || 0, + Service: accessory.getService(this.hap.Service.WindowCovering)!, + PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: accessory.context.TargetPosition || 100, + CurrentPosition: accessory.context.CurrentPosition || 100, + TargetHorizontalTiltAngle: accessory.context.TargetHorizontalTiltAngle || 0, + CurrentHorizontalTiltAngle: accessory.context.CurrentHorizontalTiltAngle || 0, }; // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), - BatteryLevel: this.accessory.context.BatteryLevel || 100, + Service: accessory.getService(this.hap.Service.Battery)!, + BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; // Initialize LightSensor property if (!this.device.blindTilt?.hide_lightsensor) { this.LightSensor = { - Service: this.accessory.addService(this.hap.Service.LightSensor), - CurrentAmbientLightLevel: this.accessory.context.CurrentAmbientLightLevel || 0, + Service: accessory.getService(this.hap.Service.LightSensor)!, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the WindowCovering service if it exists, otherwise create a new WindowCovering service // you can create multiple services for each accessory (this.WindowCovering!.Service = @@ -262,16 +262,18 @@ export class BlindTilt extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + - ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); if (!this.device.blindTilt?.hide_lightsensor) { const set_minLux = await this.minLux(); const set_maxLux = await this.maxLux(); const spaceBetweenLevels = 9; + if (this.LightSensor?.CurrentAmbientLightLevel === 0) { + this.LightSensor!.CurrentAmbientLightLevel = 0.0001; + } + // Brightness switch (serviceData.lightLevel) { case 1: @@ -347,7 +349,7 @@ export class BlindTilt extends deviceBase { String(deviceStatus.body.direction)); this.debugLog(` device: ${deviceStatus.body.slidePosition} => HK: ${homekitPosition}`); - this.WindowCovering.CurrentPosition = homekitPosition; + this.WindowCovering!.CurrentPosition = homekitPosition; // CurrentPosition await this.setMinMax(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); diff --git a/src/device/bot.ts b/src/device/bot.ts index d9e5c8a3..560795fa 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -98,12 +98,9 @@ export class Bot extends deviceBase { this.doBotUpdate = new Subject(); this.botUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Battery property this.Battery = { - Service: accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -112,7 +109,7 @@ export class Bot extends deviceBase { if (this.botDeviceType === 'switch') { // Initialize Switch property this.Switch = { - Service: this.accessory.addService(this.hap.Service.Switch), + Service: accessory.getService(this.hap.Service.Switch)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -136,7 +133,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'garagedoor') { // Initialize Switch property this.GarageDoor = { - Service: this.accessory.addService(this.hap.Service.GarageDoorOpener), + Service: accessory.getService(this.hap.Service.GarageDoorOpener)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -161,7 +158,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'door') { // Initialize Switch property this.Door = { - Service: this.accessory.addService(this.hap.Service.Door), + Service: accessory.getService(this.hap.Service.Door)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -194,7 +191,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'window') { // Initialize Switch property this.Window = { - Service: this.accessory.addService(this.hap.Service.Window), + Service: accessory.getService(this.hap.Service.Window)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -227,7 +224,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'windowcovering') { // Initialize Switch property this.WindowCovering = { - Service: this.accessory.addService(this.hap.Service.WindowCovering), + Service: accessory.getService(this.hap.Service.WindowCovering)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -260,7 +257,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'lock') { // Initialize Switch property this.Lock = { - Service: this.accessory.addService(this.hap.Service.LockMechanism), + Service: accessory.getService(this.hap.Service.LockMechanism)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -284,7 +281,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'faucet') { // Initialize Switch property this.Faucet = { - Service: this.accessory.addService(this.hap.Service.Faucet), + Service: accessory.getService(this.hap.Service.Faucet)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -308,7 +305,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'fan') { // Initialize Switch property this.Fan = { - Service: this.accessory.addService(this.hap.Service.Fanv2), + Service: accessory.getService(this.hap.Service.Fanv2)!, On: accessory.context.On || false, }; this.removeLockService(accessory); @@ -332,7 +329,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'stateful') { // Initialize Switch property this.StatefulProgrammableSwitch = { - Service: this.accessory.addService(this.hap.Service.StatefulProgrammableSwitch), + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -358,7 +355,7 @@ export class Bot extends deviceBase { } else { // Initialize Switch property this.Outlet = { - Service: this.accessory.addService(this.hap.Service.Outlet), + Service: accessory.getService(this.hap.Service.Outlet)!, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -381,6 +378,9 @@ export class Bot extends deviceBase { this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // batteryService const batteryService = `${accessory.displayName} Battery`; (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 142ab2b6..8b3364f9 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -48,12 +48,9 @@ export class CeilingLight extends deviceBase { this.doCeilingLightUpdate = new Subject(); this.ceilingLightUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize LightBulb property this.LightBulb = { - Service: this.accessory.addService(this.hap.Service.Lightbulb), + Service: accessory.getService(this.hap.Service.Lightbulb)!, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, @@ -61,6 +58,9 @@ export class CeilingLight extends deviceBase { ColorTemperature: accessory.context.ColorTemperature || 140, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + const LightBulbService = `${accessory.displayName} ${device.deviceType}`; (this.LightBulb!.Service = accessory.getService(this.hap.Service.Lightbulb) || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index ceedcc98..94a87fc3 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -49,12 +49,9 @@ export class ColorBulb extends deviceBase { this.doColorBulbUpdate = new Subject(); this.colorBulbUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize LightBulb property this.LightBulb = { - Service: this.accessory.addService(this.hap.Service.Lightbulb), + Service: accessory.getService(this.hap.Service.Lightbulb)!, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, @@ -62,6 +59,9 @@ export class ColorBulb extends deviceBase { ColorTemperature: accessory.context.ColorTemperature || 140, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory const lightBulbService = `${accessory.displayName} ${device.deviceType}`; diff --git a/src/device/contact.ts b/src/device/contact.ts index 0f9cfb31..363cb568 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -51,19 +51,16 @@ export class Contact extends deviceBase { this.doContactUpdate = new Subject(); this.contactUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Contact Sensor property this.ContactSensor = { - Service: this.accessory.addService(this.hap.Service.ContactSensor), + Service: accessory.getService(this.hap.Service.ContactSensor)!, ContactSensorState: this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; // Initialize Motion Sensor property if (!this.device.contact?.hide_motionsensor) { this.MotionSensor = { - Service: this.accessory.addService(this.hap.Service.MotionSensor), + Service: accessory.getService(this.hap.Service.MotionSensor)!, MotionDetected: false, }; } @@ -71,11 +68,14 @@ export class Contact extends deviceBase { // Initialize Light Sensor property if (!this.device.contact?.hide_lightsensor) { this.LightSensor = { - Service: this.accessory.addService(this.hap.Service.LightSensor), + Service: accessory.getService(this.hap.Service.LightSensor)!, CurrentAmbientLightLevel: 0, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the Contact service if it exists, otherwise create a new Contact service // you can create multiple services for each accessory const ContactSensorService = `${accessory.displayName} Contact Sensor`; diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 7efd9b81..4e33eaef 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -53,32 +53,33 @@ export class Curtain extends deviceBase { this.curtainUpdateInProgress = false; this.setNewTarget = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); // Initialize LightBulb property this.WindowCovering = { - Service: this.accessory.addService(this.hap.Service.WindowCovering), - PositionState: this.accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, - TargetPosition: this.accessory.context.TargetPosition || 100, - CurrentPosition: this.accessory.context.CurrentPosition || 100, - HoldPosition: this.accessory.context.HoldPosition || false, + Service: accessory.getService(this.hap.Service.WindowCovering)!, + PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: accessory.context.TargetPosition || 100, + CurrentPosition: accessory.context.CurrentPosition || 100, + HoldPosition: accessory.context.HoldPosition || false, }; // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), - BatteryLevel: this.accessory.context.BatteryLevel || 100, - StatusLowBattery: this.accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Service: accessory.getService(this.hap.Service.Battery)!, + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; // Initialize LightSensor property if (!this.device.curtain?.hide_lightsensor) { this.LightSensor = { - Service: this.accessory.addService(this.hap.Service.LightSensor), - CurrentAmbientLightLevel: this.accessory.context.CurrentAmbientLightLevel || 0, + Service: accessory.getService(this.hap.Service.LightSensor)!, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the WindowCovering service if it exists, otherwise create a new WindowCovering service // you can create multiple services for each accessory const WindowCoveringService = `${accessory.displayName} ${device.deviceType}`; @@ -393,7 +394,7 @@ export class Curtain extends deviceBase { async openAPIparseStatus(deviceStatus: deviceStatus): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); // CurrentPosition - this.WindowCovering.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition); + this.WindowCovering!.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition); await this.setMinMax(); this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`); if (this.setNewTarget) { diff --git a/src/device/hub.ts b/src/device/hub.ts index daf50c75..5d5f6c68 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -38,13 +38,11 @@ export class Hub extends deviceBase { // this is subject we use to track when we need to POST changes to the SwitchBot API this.doHubUpdate = new Subject(); this.hubUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); // Initialize Temperature Sensor property if (!device.hub?.hide_temperature) { this.TemperatureSensor = { - Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, CurrentTemperature: accessory.context.CurrentTemperature || 0, }; } @@ -52,7 +50,7 @@ export class Hub extends deviceBase { // Initialize Humidity Sensor property if (!device.hub?.hide_humidity) { this.HumiditySensor = { - Service: this.accessory.addService(this.hap.Service.HumiditySensor), + Service: accessory.getService(this.hap.Service.HumiditySensor)!, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 0, }; } @@ -60,11 +58,14 @@ export class Hub extends deviceBase { // Initialize Light Sensor property if (!device.hub?.hide_lightsensor) { this.LightSensor = { - Service: this.accessory.addService(this.hap.Service.LightSensor), - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0, + Service: accessory.getService(this.hap.Service.LightSensor)!, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Temperature Sensor Service if (device.hub?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 90f864ae..458a370b 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -42,12 +42,9 @@ export class Humidifier extends deviceBase { this.doHumidifierUpdate = new Subject(); this.humidifierUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize the HumidifierDehumidifier Service this.HumidifierDehumidifier = { - Service: accessory.addService(this.hap.Service.HumidifierDehumidifier), + Service: accessory.getService(this.hap.Service.HumidifierDehumidifier)!, Active: accessory.context.Active || this.hap.Characteristic.Active.ACTIVE, WaterLevel: accessory.context.WaterLevel || 100, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, @@ -61,11 +58,14 @@ export class Humidifier extends deviceBase { // Initialize the Temperature Sensor Service if (!device.humidifier?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.addService(this.hap.Service.TemperatureSensor), + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the service if it exists, otherwise create a new service // you can create multiple services for each accessory const HumidifierDehumidifierService = `${accessory.displayName} Humidifier`; diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 2f1068b1..e3c0fe1b 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -45,13 +45,10 @@ export class IOSensor extends deviceBase { this.doIOSensorUpdate = new Subject(); this.ioSensorUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Temperature Sensor property if (!device.iosensor?.hide_temperature) { this.TemperatureSensor = { - Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -59,18 +56,21 @@ export class IOSensor extends deviceBase { // Initialize Humidity Sensor property if (!device.iosensor?.hide_humidity) { this.HumiditySensor = { - Service: this.accessory.addService(this.hap.Service.HumiditySensor), + Service: accessory.getService(this.hap.Service.HumiditySensor)!, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Temperature Sensor Service if (device.iosensor?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 55270ba7..c396e3ff 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -42,12 +42,9 @@ export class StripLight extends deviceBase { this.doStripLightUpdate = new Subject(); this.stripLightUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize the LightBulb property this.LightBulb = { - Service: this.accessory.addService(this.hap.Service.Lightbulb), + Service: accessory.getService(this.hap.Service.Lightbulb)!, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, @@ -55,6 +52,9 @@ export class StripLight extends deviceBase { ColorTemperature: accessory.context.ColorTemperature || 140, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory const LightBulbService = `${accessory.displayName} ${device.deviceType}`; diff --git a/src/device/lock.ts b/src/device/lock.ts index 601c3af1..5f8f98f7 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -44,19 +44,16 @@ export class Lock extends deviceBase { this.doLockUpdate = new Subject(); this.lockUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize LockMechanism property this.LockMechanism = { - Service: accessory.addService(this.hap.Service.LockMechanism), + Service: accessory.getService(this.hap.Service.LockMechanism)!, LockTargetState: accessory.context.LockTargetState || this.hap.Characteristic.LockTargetState.SECURED, LockCurrentState: accessory.context.LockCurrentState || this.hap.Characteristic.LockCurrentState.SECURED, }; // Initialize Battery property this.Battery = { - Service: accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -64,7 +61,7 @@ export class Lock extends deviceBase { // Initialize ContactSensor property if (!this.device.lock?.hide_contactsensor) { this.ContactSensor = { - Service: accessory.addService(this.hap.Service.ContactSensor), + Service: accessory.getService(this.hap.Service.ContactSensor)!, ContactSensorState: accessory.context.ContactSensorState || this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; } @@ -72,11 +69,14 @@ export class Lock extends deviceBase { // Initialize Latch Button Service if (device.lock?.activate_latchbutton) { this.Switch = { - Service: accessory.addService(this.hap.Service.Switch), + Service: accessory.getService(this.hap.Service.Switch)!, On: accessory.context.On || false, }; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the LockMechanism service if it exists, otherwise create a new LockMechanism service // you can create multiple services for each accessory const LockMechanismService = `${accessory.displayName} ${device.deviceType}`; diff --git a/src/device/meter.ts b/src/device/meter.ts index f65e837c..5da1950a 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -40,13 +40,10 @@ export class Meter extends deviceBase { this.doMeterUpdate = new Subject(); this.meterUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Temperature Sensor property if (!device.meter?.hide_temperature) { this.TemperatureSensor = { - Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -54,18 +51,21 @@ export class Meter extends deviceBase { // Initialize Humidity Sensor property if (!device.meter?.hide_humidity) { this.HumiditySensor = { - Service: this.accessory.addService(this.hap.Service.HumiditySensor), + Service: accessory.getService(this.hap.Service.HumiditySensor)!, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index d14aa02c..bb09e095 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -45,13 +45,10 @@ export class MeterPlus extends deviceBase { this.doMeterUpdate = new Subject(); this.meterUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Temperature Sensor property if (!device.meter?.hide_temperature) { this.TemperatureSensor = { - Service: this.accessory.addService(this.hap.Service.TemperatureSensor), + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -59,18 +56,21 @@ export class MeterPlus extends deviceBase { // Initialize Humidity Sensor property if (!device.meter?.hide_humidity) { this.HumiditySensor = { - Service: this.accessory.addService(this.hap.Service.HumiditySensor), + Service: accessory.getService(this.hap.Service.HumiditySensor)!, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); diff --git a/src/device/motion.ts b/src/device/motion.ts index 37291916..18f33627 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -46,15 +46,15 @@ export class Motion extends deviceBase { this.doMotionUpdate = new Subject(); this.motionUbpdateInProgress = false; + // Initialize Motion Sensor property + this.MotionSensor = { + Service: accessory.getService(this.hap.Service.MotionSensor)!, + MotionDetected:accessory.context.MotionDetected || false, + }; + // Retrieve initial values and updateHomekit this.refreshStatus(); - if (this.MotionSensor.MotionDetected === undefined) { - this.MotionSensor.MotionDetected = false; - } else { - this.MotionSensor.MotionDetected = this.accessory.context.MotionDetected; - } - // get the Battery service if it exists, otherwise create a new Motion service // you can create multiple services for each accessory const MotionSensorService = `${accessory.displayName} Motion Sensor`; diff --git a/src/device/plug.ts b/src/device/plug.ts index 69c8d8b7..d69cab07 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -29,15 +29,15 @@ export class Plug extends deviceBase { this.doPlugUpdate = new Subject(); this.plugUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Outlet property this.Outlet = { - Service: this.accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Outlet)!, On: accessory.context.On || false, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the Outlet service if it exists, otherwise create a new Outlet service // you can create multiple services for each accessory const OutletService = `${accessory.displayName} ${device.deviceType}`; diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index f2c2b78f..ce69f979 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -38,23 +38,23 @@ export class RobotVacuumCleaner extends deviceBase { this.doRobotVacuumCleanerUpdate = new Subject(); this.robotVacuumCleanerUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Lightbulb property this.LightBulb = { - Service: this.accessory.addService(this.hap.Service.Lightbulb), + Service: accessory.getService(this.hap.Service.Lightbulb)!, On: accessory.context.On || false, Brightness: accessory.context.Brightness || 0, }; // Initialize Battery property this.Battery = { - Service: this.accessory.addService(this.hap.Service.Battery), + Service: accessory.getService(this.hap.Service.Battery)!, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // get the Lightbulb service if it exists, otherwise create a new Lightbulb service // you can create multiple services for each accessory const LightBulbService = `${accessory.displayName} ${device.deviceType}`; diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index ae993415..cdc2c4d0 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -46,33 +46,25 @@ export class WaterDetector extends deviceBase { this.doWaterDetectorUpdate = new Subject(); this.WaterDetectorUpdateInProgress = false; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - // Initialize Battery property this.Battery = { - Service: this.accessory.getService(this.hap.Service.Battery) || this.accessory.addService(this.hap.Service.Battery), - BatteryLevel: this.accessory.context.BatteryLevel || 100, - StatusLowBattery: this.accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - ChargingState: this.accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, + Service: accessory.getService(this.hap.Service.Battery)!, + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, }; // Initialize Leak Sensor property if (!this.device.waterdetector?.hide_leak) { this.LeakSensor = { - Service: this.accessory.getService(this.hap.Service.LeakSensor) || this.accessory.addService(this.hap.Service.LeakSensor), - StatusActive: this.accessory.context.StatusActive || false, - LeakDetected: this.accessory.context.LeakDetected || this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, + Service: accessory.getService(this.hap.Service.LeakSensor)!, + StatusActive: accessory.context.StatusActive || false, + LeakDetected: accessory.context.LeakDetected || this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, }; } - // set accessory information - accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Model, 'WoWaterDetector') - .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Leak Sensor Service if (device.waterdetector?.hide_leak) { diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index dccc6ee8..83f79338 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -15,16 +15,20 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class AirConditioner extends irdeviceBase { // Services - coolerService!: Service; - - // Characteristic Values - Active!: CharacteristicValue; - RotationSpeed!: CharacteristicValue; - CurrentTemperature!: CharacteristicValue; - ThresholdTemperature!: CharacteristicValue; - CurrentRelativeHumidity?: CharacteristicValue; - TargetHeaterCoolerState!: CharacteristicValue; - CurrentHeaterCoolerState!: CharacteristicValue; + private HeaterCooler: { + Service: Service; + Active: CharacteristicValue; + CurrentHeaterCoolerState: CharacteristicValue; + TargetHeaterCoolerState: CharacteristicValue; + CurrentTemperature: CharacteristicValue; + ThresholdTemperature: CharacteristicValue; + RotationSpeed: CharacteristicValue; + }; + + private HumiditySensor?: { + Service: Service; + CurrentRelativeHumidity: CharacteristicValue; + }; // Others state!: string; @@ -33,9 +37,6 @@ export class AirConditioner extends irdeviceBase { CurrentMode!: number; ValidValues: number[]; CurrentFanSpeed!: number; - static MODE_AUTO: number; - static MODE_COOL: number; - static MODE_HEAT: number; // Config hide_automode?: boolean; @@ -45,9 +46,6 @@ export class AirConditioner extends irdeviceBase { set_min_cool?: number; meter?: PlatformAccessory; - private readonly valid12 = [1, 2]; - private readonly valid012 = [0, 1, 2]; - constructor( readonly platform: SwitchBotPlatform, accessory: PlatformAccessory, @@ -55,90 +53,44 @@ export class AirConditioner extends irdeviceBase { ) { super(platform, accessory, device); - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else if (this.Active) { - this.Active; - } else { - this.Active = this.accessory.context.Active; - } - - if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; - } else { - this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; - } - - if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { - this.ThresholdTemperature = 24; - } else { - this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; - } - - if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { - this.RotationSpeed = 4; - } else { - this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; - } - - if (this.device.irair?.hide_automode) { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } else { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } - - if (this.device.irair?.set_max_heat) { - this.set_max_heat = this.device.irair?.set_max_heat; - this.accessory.context.set_max_heat = this.set_max_heat; - } else { - this.set_max_heat = 35; - this.accessory.context.set_max_heat = this.set_max_heat; - } - if (this.device.irair?.set_min_heat) { - this.set_min_heat = this.device.irair?.set_min_heat; - this.accessory.context.set_min_heat = this.set_min_heat; - } else { - this.set_min_heat = 0; - this.accessory.context.set_min_heat = this.set_min_heat; - } - - if (this.device.irair?.set_max_cool) { - this.set_max_cool = this.device.irair?.set_max_cool; - this.accessory.context.set_max_cool = this.set_max_cool; - } else { - this.set_max_cool = 35; - this.accessory.context.set_max_cool = this.set_max_cool; - } - if (this.device.irair?.set_min_cool) { - this.set_min_cool = this.device.irair?.set_min_cool; - this.accessory.context.set_min_cool = this.set_min_cool; - } else { - this.set_min_cool = 0; - this.accessory.context.set_min_cool = this.set_min_cool; - } - - if (this.meter) { - if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; - } else { - this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; - } + // default placeholders + this.getAirConditionerConfigSettings(accessory, device); + + // Initialize HeaterCooler property + this.HeaterCooler = { + Service: accessory.getService(this.hap.Service.Switch)!, + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState || this.hap.Characteristic.CurrentHeaterCoolerState.IDLE, + TargetHeaterCoolerState: accessory.context.TargetHeaterCoolerState || this.hap.Characteristic.TargetHeaterCoolerState.AUTO, + CurrentTemperature: accessory.context.CurrentTemperature || 24, + ThresholdTemperature: accessory.context.ThresholdTemperature || 24, + RotationSpeed: accessory.context.RotationSpeed || 4, + }; + + // Initialize HumiditySensor property + if (this.device.irair?.meterType && this.device.irair?.meterId) { + const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); + this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); + this.HumiditySensor = { + Service: this.meter!.getService(this.hap.Service.HumiditySensor)!, + CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, + }; } // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - const coolerService = `${accessory.displayName} ${device.remoteType}`; - (this.coolerService = accessory.getService(this.hap.Service.HeaterCooler) - || accessory.addService(this.hap.Service.HeaterCooler)), coolerService; + const HeaterCoolerService = `${accessory.displayName} ${device.remoteType}`; + (this.HeaterCooler.Service = accessory.getService(this.hap.Service.HeaterCooler) + || accessory.addService(this.hap.Service.HeaterCooler)), HeaterCoolerService; - this.coolerService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.HeaterCooler.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the Active characteristic - this.coolerService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.Active) + .onSet(this.ActiveSet.bind(this)); - this.coolerService.getCharacteristic(this.hap.Characteristic.CurrentTemperature).onGet(this.CurrentTemperatureGet.bind(this)); + this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.CurrentTemperature) + .onGet(this.CurrentTemperatureGet.bind(this)); this.ValidValues = this.hide_automode ? [1, 2] : [0, 1, 2]; @@ -148,10 +100,11 @@ export class AirConditioner extends irdeviceBase { } if (this.meter) { - this.coolerService.getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity).onGet(this.CurrentRelativeHumidityGet.bind(this)); + this.HumiditySensor!.Service.getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) + .onGet(this.CurrentRelativeHumidityGet.bind(this)); } - this.coolerService + this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState) .setProps({ validValues: this.ValidValues, @@ -159,9 +112,10 @@ export class AirConditioner extends irdeviceBase { .onGet(this.TargetHeaterCoolerStateGet.bind(this)) .onSet(this.TargetHeaterCoolerStateSet.bind(this)); - this.coolerService.getCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState).onGet(this.CurrentHeaterCoolerStateGet.bind(this)); + this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState) + .onGet(this.CurrentHeaterCoolerStateGet.bind(this)); - this.coolerService + this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature) .setProps({ minValue: this.set_min_heat, @@ -171,7 +125,7 @@ export class AirConditioner extends irdeviceBase { .onGet(this.ThresholdTemperatureGet.bind(this)) .onSet(this.ThresholdTemperatureSet.bind(this)); - this.coolerService + this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature) .setProps({ minValue: this.set_min_cool, @@ -181,7 +135,7 @@ export class AirConditioner extends irdeviceBase { .onGet(this.ThresholdTemperatureGet.bind(this)) .onSet(this.ThresholdTemperatureSet.bind(this)); - this.coolerService + this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.RotationSpeed) .setProps({ format: 'int', @@ -204,10 +158,10 @@ export class AirConditioner extends irdeviceBase { */ async pushAirConditionerOnChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges Active: ${this.HeaterCooler.Active},` + ` disablePushOn: ${this.disablePushOn}`, ); - if (this.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { + if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -221,10 +175,10 @@ export class AirConditioner extends irdeviceBase { async pushAirConditionerOffChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges Active: ${this.HeaterCooler.Active},` + ` disablePushOff: ${this.disablePushOff}`, ); - if (this.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { + if (this.HeaterCooler.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -238,12 +192,12 @@ export class AirConditioner extends irdeviceBase { async pushAirConditionerStatusChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerStatusChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerStatusChanges Active: ${this.HeaterCooler.Active},` + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, ); if (!this.Busy) { this.Busy = true; - this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; + this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; } clearTimeout(this.Timeout); @@ -253,7 +207,7 @@ export class AirConditioner extends irdeviceBase { async pushAirConditionerDetailsChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerDetailsChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerDetailsChanges Active: ${this.HeaterCooler.Active},` + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, ); //await this.deviceContext(); @@ -263,20 +217,20 @@ export class AirConditioner extends irdeviceBase { if (this.CurrentFanSpeed === undefined) { this.CurrentFanSpeed = 1; } - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE) { this.state = 'on'; } else { this.state = 'off'; } if (this.CurrentMode === 1) { // Remove or make configurable? - this.ThresholdTemperature = 25; + this.HeaterCooler.ThresholdTemperature = 25; this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName} CurrentMode: ${this.CurrentMode},` + - ` ThresholdTemperature: ${this.ThresholdTemperature}`, + ` ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`, ); } - const parameter = `${this.ThresholdTemperature},${this.CurrentMode},${this.CurrentFanSpeed},${this.state}`; + const parameter = `${this.HeaterCooler.ThresholdTemperature},${this.CurrentMode},${this.CurrentFanSpeed},${this.state}`; await this.UpdateCurrentHeaterCoolerState(); const bodyChange = JSON.stringify({ @@ -289,32 +243,32 @@ export class AirConditioner extends irdeviceBase { } private async UpdateCurrentHeaterCoolerState() { - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE) { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else if (this.Active) { - this.Active; + if (this.HeaterCooler.Active === undefined) { + this.HeaterCooler.Active = this.hap.Characteristic.Active.INACTIVE; + } else if (this.HeaterCooler.Active) { + this.HeaterCooler.Active; } else { - this.Active = this.accessory.context.Active; + this.HeaterCooler.Active = this.accessory.context.Active; } - if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; + if (this.HeaterCooler.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { + this.HeaterCooler.CurrentTemperature = 24; } else { - this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; + this.HeaterCooler.CurrentTemperature = this.HeaterCooler.CurrentTemperature || this.accessory.context.CurrentTemperature; } - if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { - this.ThresholdTemperature = 24; + if (this.HeaterCooler.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { + this.HeaterCooler.ThresholdTemperature = 24; } else { - this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; + this.HeaterCooler.ThresholdTemperature = this.HeaterCooler.ThresholdTemperature || this.accessory.context.ThresholdTemperature; } - if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { - this.RotationSpeed = 4; + if (this.HeaterCooler.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { + this.HeaterCooler.RotationSpeed = 4; } else { - this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; + this.HeaterCooler.RotationSpeed = this.HeaterCooler.RotationSpeed || this.accessory.context.RotationSpeed; } if (this.device.irair?.hide_automode) { @@ -356,23 +310,24 @@ export class AirConditioner extends irdeviceBase { } if (this.meter) { - if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { + this.HumiditySensor!.CurrentRelativeHumidity = 0; } else { - this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; + this.HumiditySensor!.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity + || this.accessory.context.CurrentRelativeHumidity; } } - if (this.ThresholdTemperature < this.CurrentTemperature && - this.TargetHeaterCoolerState !== this.hap.Characteristic.TargetHeaterCoolerState.HEAT) { - this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING; - } else if (this.ThresholdTemperature > this.CurrentTemperature && - this.TargetHeaterCoolerState !== this.hap.Characteristic.TargetHeaterCoolerState.COOL) { - this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.HEATING; + if (this.HeaterCooler.ThresholdTemperature < this.HeaterCooler.CurrentTemperature && + this.HeaterCooler.TargetHeaterCoolerState !== this.hap.Characteristic.TargetHeaterCoolerState.HEAT) { + this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING; + } else if (this.HeaterCooler.ThresholdTemperature > this.HeaterCooler.CurrentTemperature && + this.HeaterCooler.TargetHeaterCoolerState !== this.hap.Characteristic.TargetHeaterCoolerState.COOL) { + this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.HEATING; } else { - this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; + this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; } } else { - this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE; + this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE; } } @@ -428,9 +383,9 @@ export class AirConditioner extends irdeviceBase { ); } - this.CurrentTemperature = this.accessory.context.CurrentTemperature || 24; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get CurrentTemperature: ${this.CurrentTemperature}`); - return this.CurrentTemperature; + this.HeaterCooler.CurrentTemperature = this.accessory.context.CurrentTemperature || 24; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get CurrentTemperature: ${this.HeaterCooler.CurrentTemperature}`); + return this.HeaterCooler.CurrentTemperature; } async CurrentRelativeHumidityGet(): Promise { @@ -442,19 +397,20 @@ export class AirConditioner extends irdeviceBase { ); } - this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity || 0; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); - return this.CurrentRelativeHumidity as CharacteristicValue; + this.HumiditySensor!.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity || 0; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); + return this.HumiditySensor!.CurrentRelativeHumidity as CharacteristicValue; } async RotationSpeedGet(): Promise { if (!this.CurrentFanSpeed || this.CurrentFanSpeed === 1) { - this.RotationSpeed = 4; + this.HeaterCooler.RotationSpeed = 4; } else { - this.RotationSpeed = this.CurrentFanSpeed - 1; + this.HeaterCooler.RotationSpeed = this.CurrentFanSpeed - 1; } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get RotationSpeed: ${this.RotationSpeed}`); - return this.RotationSpeed; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); + return this.HeaterCooler.RotationSpeed; } async RotationSpeedSet(value: CharacteristicValue): Promise { @@ -463,37 +419,35 @@ export class AirConditioner extends irdeviceBase { } else { this.CurrentFanSpeed = Number(value) + 1; } - this.RotationSpeed = value; + this.HeaterCooler.RotationSpeed = value; this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + - `Set RotationSpeed: ${this.RotationSpeed}, CurrentFanSpeed: ${this.CurrentFanSpeed}`); + `Set RotationSpeed: ${this.HeaterCooler.RotationSpeed}, CurrentFanSpeed: ${this.CurrentFanSpeed}`); this.pushAirConditionerStatusChanges(); } async ActiveSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set Active: ${value}`); - this.Active = value; - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges, Active: ${this.Active}`); + this.HeaterCooler.Active = value; + if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges, Active: ${this.HeaterCooler.Active}`); if (this.disablePushOn) { this.pushAirConditionerStatusChanges(); } else { this.pushAirConditionerOnChanges(); } } else { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges, Active: ${this.Active}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges, Active: ${this.HeaterCooler.Active}`); this.pushAirConditionerOffChanges(); } } async TargetHeaterCoolerStateGet(): Promise { - const targetState = this.TargetHeaterCoolerState || this.accessory.context.TargetHeaterCoolerState; - this.TargetHeaterCoolerState = this.ValidValues.includes(targetState) ? targetState : this.ValidValues[0]; - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} Get (${this.getTargetHeaterCoolerStateName()})` + - ` TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}, ValidValues: ${this.ValidValues}, hide_automode: ${this.hide_automode}`, - ); - return this.TargetHeaterCoolerState; + const targetState = this.HeaterCooler.TargetHeaterCoolerState || this.accessory.context.TargetHeaterCoolerState; + this.HeaterCooler.TargetHeaterCoolerState = this.ValidValues.includes(targetState) ? targetState : this.ValidValues[0]; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get (${this.getTargetHeaterCoolerStateName()}) TargetHeaterCoolerState:` + + ` ${this.HeaterCooler.TargetHeaterCoolerState}, ValidValues: ${this.ValidValues}, hide_automode: ${this.hide_automode}`); + return this.HeaterCooler.TargetHeaterCoolerState; } async TargetHeaterCoolerStateSet(value: CharacteristicValue): Promise { @@ -505,7 +459,7 @@ export class AirConditioner extends irdeviceBase { this.TargetHeaterCoolerStateCOOL(); } else { this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Set TargetHeaterCoolerState: ${this.TargetHeaterCoolerState},` + + `${this.device.remoteType}: ${this.accessory.displayName} Set TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState},` + ` hide_automode: ${this.hide_automode} `, ); } @@ -513,23 +467,26 @@ export class AirConditioner extends irdeviceBase { } async TargetHeaterCoolerStateAUTO(): Promise { - this.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.AUTO; + this.HeaterCooler.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.AUTO; this.CurrentMode = 1; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (AUTO) TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (AUTO)` + + ` TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Switchbot CurrentMode: ${this.CurrentMode}`); } async TargetHeaterCoolerStateCOOL(): Promise { - this.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.COOL; + this.HeaterCooler.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.COOL; this.CurrentMode = 2; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (COOL) TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (COOL)` + + ` TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Switchbot CurrentMode: ${this.CurrentMode}`); } async TargetHeaterCoolerStateHEAT(): Promise { - this.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.HEAT; + this.HeaterCooler.TargetHeaterCoolerState = this.hap.Characteristic.TargetHeaterCoolerState.HEAT; this.CurrentMode = 5; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (HEAT) TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set (HEAT)` + + ` TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Switchbot CurrentMode: ${this.CurrentMode}`); } @@ -537,15 +494,15 @@ export class AirConditioner extends irdeviceBase { await this.UpdateCurrentHeaterCoolerState(); this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Get (${this.getTargetHeaterCoolerStateName()}) CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`, + ` Get (${this.getTargetHeaterCoolerStateName()}) CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`, ); - return this.CurrentHeaterCoolerState; + return this.HeaterCooler.CurrentHeaterCoolerState; } private getTargetHeaterCoolerStateName(): string { - switch (this.TargetHeaterCoolerState) { + switch (this.HeaterCooler.TargetHeaterCoolerState) { case this.hap.Characteristic.TargetHeaterCoolerState.AUTO: return 'AUTO'; case this.hap.Characteristic.TargetHeaterCoolerState.HEAT: @@ -553,36 +510,36 @@ export class AirConditioner extends irdeviceBase { case this.hap.Characteristic.TargetHeaterCoolerState.COOL: return 'COOL'; default: - return this.TargetHeaterCoolerState.toString(); + return this.HeaterCooler.TargetHeaterCoolerState.toString(); } } async ThresholdTemperatureGet(): Promise { - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else if (this.Active) { - this.Active; + if (this.HeaterCooler.Active === undefined) { + this.HeaterCooler.Active = this.hap.Characteristic.Active.INACTIVE; + } else if (this.HeaterCooler.Active) { + this.HeaterCooler.Active; } else { - this.Active = this.accessory.context.Active; + this.HeaterCooler.Active = this.accessory.context.Active; } - if (this.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; + if (this.HeaterCooler.CurrentTemperature === undefined && this.accessory.context.CurrentTemperature === undefined) { + this.HeaterCooler.CurrentTemperature = 24; } else { - this.CurrentTemperature = this.CurrentTemperature || this.accessory.context.CurrentTemperature; + this.HeaterCooler.CurrentTemperature = this.HeaterCooler.CurrentTemperature || this.accessory.context.CurrentTemperature; } - if (this.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { - this.ThresholdTemperature = 24; + if (this.HeaterCooler.ThresholdTemperature === undefined && this.accessory.context.ThresholdTemperature === undefined) { + this.HeaterCooler.ThresholdTemperature = 24; } else { - this.ThresholdTemperature = this.ThresholdTemperature || this.accessory.context.ThresholdTemperature; + this.HeaterCooler.ThresholdTemperature = this.HeaterCooler.ThresholdTemperature || this.accessory.context.ThresholdTemperature; } - if (this.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { - this.RotationSpeed = 4; + if (this.HeaterCooler.RotationSpeed === undefined && this.accessory.context.RotationSpeed === undefined) { + this.HeaterCooler.RotationSpeed = 4; } else { - this.RotationSpeed = this.RotationSpeed || this.accessory.context.RotationSpeed; + this.HeaterCooler.RotationSpeed = this.HeaterCooler.RotationSpeed || this.accessory.context.RotationSpeed; } if (this.device.irair?.hide_automode) { @@ -624,20 +581,20 @@ export class AirConditioner extends irdeviceBase { } if (this.meter) { - if (this.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { - this.CurrentRelativeHumidity = 0; + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { + this.HumiditySensor!.CurrentRelativeHumidity = 0; } else { - this.CurrentRelativeHumidity = this.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; + this.HumiditySensor!.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity || this.accessory.context.CurrentRelativeHumidity; } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get ThresholdTemperature: ${this.ThresholdTemperature}`); - return this.ThresholdTemperature; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Get ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); + return this.HeaterCooler.ThresholdTemperature; } async ThresholdTemperatureSet(value: CharacteristicValue): Promise { - this.ThresholdTemperature = value; + this.HeaterCooler.ThresholdTemperature = value; this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} Set ThresholdTemperature: ${this.ThresholdTemperature},` + + `${this.device.remoteType}: ${this.accessory.displayName} Set ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature},` + ` ThresholdTemperatureCached: ${this.accessory.context.ThresholdTemperature}`, ); this.pushAirConditionerStatusChanges(); @@ -645,83 +602,127 @@ export class AirConditioner extends irdeviceBase { async updateHomeKitCharacteristics(): Promise { // Active - if (this.Active === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); + if (this.HeaterCooler.Active === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.HeaterCooler.Active}`); } else { - this.accessory.context.Active = this.Active; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + this.accessory.context.Active = this.HeaterCooler.Active; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.HeaterCooler.Active); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.HeaterCooler.Active}`); } // RotationSpeed - if (this.RotationSpeed === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.RotationSpeed}`); + if (this.HeaterCooler.RotationSpeed === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); } else { - this.accessory.context.RotationSpeed = this.RotationSpeed; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.RotationSpeed); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.RotationSpeed}`); + this.accessory.context.RotationSpeed = this.HeaterCooler.RotationSpeed; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.HeaterCooler.RotationSpeed); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); } // CurrentTemperature - if (this.CurrentTemperature === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + if (this.HeaterCooler.CurrentTemperature === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentTemperature: ${this.HeaterCooler.CurrentTemperature}`); } else { - this.accessory.context.CurrentTemperature = this.CurrentTemperature; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + this.accessory.context.CurrentTemperature = this.HeaterCooler.CurrentTemperature; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.HeaterCooler.CurrentTemperature); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.HeaterCooler.CurrentTemperature}`); } // CurrentRelativeHumidity if (this.meter) { - if (this.CurrentRelativeHumidity === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { - this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); + this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HumiditySensor!.CurrentRelativeHumidity); this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`, + `${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`, ); } } // TargetHeaterCoolerState - if (this.TargetHeaterCoolerState === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + if (this.HeaterCooler.TargetHeaterCoolerState === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); } else { - this.accessory.context.TargetHeaterCoolerState = this.TargetHeaterCoolerState; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, this.TargetHeaterCoolerState); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`, - ); + this.accessory.context.TargetHeaterCoolerState = this.HeaterCooler.TargetHeaterCoolerState; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, this.HeaterCooler.TargetHeaterCoolerState); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); } // CurrentHeaterCoolerState - if (this.CurrentHeaterCoolerState === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); + if (this.HeaterCooler.CurrentHeaterCoolerState === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); } else { - this.accessory.context.CurrentHeaterCoolerState = this.CurrentHeaterCoolerState; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); + this.accessory.context.CurrentHeaterCoolerState = this.HeaterCooler.CurrentHeaterCoolerState; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.HeaterCooler.CurrentHeaterCoolerState); this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`, + ` updateCharacteristic CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`, ); } // ThresholdTemperature - if (this.ThresholdTemperature === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ThresholdTemperature: ${this.ThresholdTemperature}`); + if (this.HeaterCooler.ThresholdTemperature === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); } else { - this.accessory.context.ThresholdTemperature = this.ThresholdTemperature; - this.coolerService?.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, this.ThresholdTemperature); - this.coolerService?.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, this.ThresholdTemperature); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic ThresholdTemperature: ${this.ThresholdTemperature}`, - ); + this.accessory.context.ThresholdTemperature = this.HeaterCooler.ThresholdTemperature; + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); + this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); } } async apiError({ e }: { e: any }): Promise { - this.coolerService.updateCharacteristic(this.hap.Characteristic.Active, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, e); - this.coolerService.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, e); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, e); + } + + async getAirConditionerConfigSettings(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): Promise { + if (this.device.irair?.hide_automode) { + this.hide_automode = device.irair?.hide_automode; + accessory.context.hide_automode = this.hide_automode; + } else { + this.hide_automode = device.irair?.hide_automode; + accessory.context.hide_automode = this.hide_automode; + } + + if (this.device.irair?.set_max_heat) { + this.set_max_heat = device.irair?.set_max_heat; + accessory.context.set_max_heat = this.set_max_heat; + } else { + this.set_max_heat = 35; + accessory.context.set_max_heat = this.set_max_heat; + } + if (this.device.irair?.set_min_heat) { + this.set_min_heat = device.irair?.set_min_heat; + accessory.context.set_min_heat = this.set_min_heat; + } else { + this.set_min_heat = 0; + accessory.context.set_min_heat = this.set_min_heat; + } + + if (this.device.irair?.set_max_cool) { + this.set_max_cool = device.irair?.set_max_cool; + accessory.context.set_max_cool = this.set_max_cool; + } else { + this.set_max_cool = 35; + accessory.context.set_max_cool = this.set_max_cool; + } + if (this.device.irair?.set_min_cool) { + this.set_min_cool = device.irair?.set_min_cool; + accessory.context.set_min_cool = this.set_min_cool; + } else { + this.set_min_cool = 0; + accessory.context.set_min_cool = this.set_min_cool; + } } } diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index e7c70f10..1cadd0d7 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -15,17 +15,24 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class AirPurifier extends irdeviceBase { // Services - airPurifierService!: Service; + private AirPurifier: { + Service: Service; + Active: CharacteristicValue; + RotationSpeed: CharacteristicValue; + CurrentAirPurifierState: CharacteristicValue; + TargetAirPurifierState: CharacteristicValue; + }; + + private TemperatureSensor: { + Service: Service; + CurrentTemperature: CharacteristicValue; + }; // Characteristic Values - Active!: CharacteristicValue; APActive!: CharacteristicValue; CurrentAPTemp!: CharacteristicValue; CurrentAPMode!: CharacteristicValue; - RotationSpeed!: CharacteristicValue; CurrentAPFanSpeed!: CharacteristicValue; - CurrentTemperature!: CharacteristicValue; - CurrentAirPurifierState!: CharacteristicValue; CurrentHeaterCoolerState!: CharacteristicValue; // Others @@ -45,40 +52,43 @@ export class AirPurifier extends irdeviceBase { ) { super(platform, accessory, device); - // default placeholders - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } - if (this.CurrentTemperature === undefined) { - this.CurrentTemperature = 24; - } else { - this.CurrentTemperature = this.accessory.context.CurrentTemperature; - } + // Initialize AirPurifier property + this.AirPurifier = { + Service: accessory.getService(this.hap.Service.Switch)!, + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + RotationSpeed: accessory.context.RotationSpeed || 0, + CurrentAirPurifierState: accessory.context.CurrentAirPurifierState || this.hap.Characteristic.CurrentAirPurifierState.INACTIVE, + TargetAirPurifierState: accessory.context.TargetAirPurifierState || this.hap.Characteristic.TargetAirPurifierState.AUTO, + }; + + // Initialize TemperatureSensor property + this.TemperatureSensor = { + Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + CurrentTemperature: accessory.context.CurrentTemperature || 24, + }; // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - const airPurifierService = `${accessory.displayName} Air Purifier`; - (this.airPurifierService = accessory.getService(this.hap.Service.AirPurifier) - || accessory.addService(this.hap.Service.AirPurifier)), airPurifierService; + const AirPurifierService = `${accessory.displayName} Air Purifier`; + (this.AirPurifier.Service = accessory.getService(this.hap.Service.AirPurifier) + || accessory.addService(this.hap.Service.AirPurifier)), AirPurifierService; - this.airPurifierService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.AirPurifier.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the Active characteristic - this.airPurifierService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - this.airPurifierService.getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState).onGet(() => { + this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState).onGet(() => { return this.CurrentAirPurifierStateGet(); }); - this.airPurifierService.getCharacteristic(this.hap.Characteristic.TargetAirPurifierState).onSet(this.TargetAirPurifierStateSet.bind(this)); + this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.TargetAirPurifierState).onSet(this.TargetAirPurifierStateSet.bind(this)); } async ActiveSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set Active: ${value}`); - this.Active = value; - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + this.AirPurifier.Active = value; + if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE) { this.pushAirPurifierOnChanges(); } else { this.pushAirPurifierOffChanges(); @@ -102,12 +112,12 @@ export class AirPurifier extends irdeviceBase { } async CurrentAirPurifierStateGet(): Promise { - if (this.Active === 1) { - this.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR; + if (this.AirPurifier.Active === 1) { + this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR; } else { - this.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.INACTIVE; + this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.INACTIVE; } - return this.CurrentAirPurifierState; + return this.AirPurifier.CurrentAirPurifierState; } /** @@ -123,10 +133,10 @@ export class AirPurifier extends irdeviceBase { */ async pushAirPurifierOnChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOnChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOnChanges Active: ${this.AirPurifier.Active},` + ` disablePushOn: ${this.disablePushOn}`, ); - if (this.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { + if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -140,10 +150,10 @@ export class AirPurifier extends irdeviceBase { async pushAirPurifierOffChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOffChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOffChanges Active: ${this.AirPurifier.Active},` + ` disablePushOff: ${this.disablePushOff}`, ); - if (this.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOn) { + if (this.AirPurifier.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -157,7 +167,7 @@ export class AirPurifier extends irdeviceBase { async pushAirPurifierStatusChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierStatusChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierStatusChanges Active: ${this.AirPurifier.Active},` + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, ); if (!this.Busy) { @@ -172,21 +182,21 @@ export class AirPurifier extends irdeviceBase { async pushAirPurifierDetailsChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierDetailsChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierDetailsChanges Active: ${this.AirPurifier.Active},` + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, ); - this.CurrentAPTemp = this.CurrentTemperature || 24; + this.CurrentAPTemp = this.TemperatureSensor!.CurrentTemperature || 24; this.CurrentAPMode = this.CurrentMode || 1; this.CurrentAPFanSpeed = this.CurrentFanSpeed || 1; - this.APActive = this.Active === 1 ? 'on' : 'off'; + this.APActive = this.AirPurifier.Active === 1 ? 'on' : 'off'; const parameter = `${this.CurrentAPTemp},${this.CurrentAPMode},${this.CurrentAPFanSpeed},${this.APActive}`; const bodyChange = JSON.stringify({ command: 'setAll', parameter: `${parameter}`, commandType: 'command', }); - if (this.Active === 1) { - if ((Number(this.CurrentTemperature) || 24) < (this.LastTemperature || 30)) { + if (this.AirPurifier.Active === 1) { + if ((Number(this.TemperatureSensor!.CurrentTemperature) || 24) < (this.LastTemperature || 30)) { this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING; } else { this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.HEATING; @@ -237,40 +247,49 @@ export class AirPurifier extends irdeviceBase { async updateHomeKitCharacteristics(): Promise { // Active - if (this.Active === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); + if (this.AirPurifier.Active === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.AirPurifier.Active}`); } else { - this.accessory.context.Active = this.Active; - this.airPurifierService?.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + this.accessory.context.Active = this.AirPurifier.Active; + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.Active, this.AirPurifier.Active); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.AirPurifier.Active}`); } // CurrentAirPurifierState - if (this.CurrentAirPurifierState === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentAirPurifierState: ${this.CurrentAirPurifierState}`); + if (this.AirPurifier.CurrentAirPurifierState === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentAirPurifierState: ${this.AirPurifier.CurrentAirPurifierState}`); } else { - this.accessory.context.CurrentAirPurifierState = this.CurrentAirPurifierState; - this.airPurifierService?.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, this.CurrentAirPurifierState); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAirPurifierState: ${this.CurrentAirPurifierState}`, - ); + this.accessory.context.CurrentAirPurifierState = this.AirPurifier.CurrentAirPurifierState; + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, this.AirPurifier.CurrentAirPurifierState); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentAirPurifierState: ${this.AirPurifier.CurrentAirPurifierState}`); } // CurrentHeaterCoolerState if (this.CurrentHeaterCoolerState === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); } else { this.accessory.context.CurrentHeaterCoolerState = this.CurrentHeaterCoolerState; - this.airPurifierService?.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`, ); } + // CurrentTemperature + if (this.TemperatureSensor.CurrentTemperature === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`); + } else { + this.accessory.context.CurrentTemperature = this.TemperatureSensor.CurrentTemperature; + this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`); + } } async apiError(e: any): Promise { - this.airPurifierService.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e); - this.airPurifierService.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, e); - this.airPurifierService.updateCharacteristic(this.hap.Characteristic.TargetAirPurifierState, e); - this.airPurifierService.updateCharacteristic(this.hap.Characteristic.Active, e); + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e); + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, e); + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.TargetAirPurifierState, e); + this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e); } } diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 19755167..f922aabb 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -15,10 +15,10 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class Camera extends irdeviceBase { // Services - switchService!: Service; - - // Characteristic Values - On!: CharacteristicValue; + private Switch: { + Service: Service; + On: CharacteristicValue; + }; constructor( readonly platform: SwitchBotPlatform, @@ -27,30 +27,29 @@ export class Camera extends irdeviceBase { ) { super(platform, accessory, device); - // default placeholders - if (this.On === undefined) { - this.On = false; - } else { - this.On = accessory.context.On; - } + // Initialize Switch property + this.Switch = { + Service: accessory.getService(this.hap.Service.Switch)!, + On: accessory.context.On || false, + }; // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - const switchService = `${accessory.displayName} Camera`; - (this.switchService = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), switchService; + const SwitchService = `${accessory.displayName} Camera`; + (this.Switch.Service = accessory.getService(this.hap.Service.Switch) + || accessory.addService(this.hap.Service.Switch)), SwitchService; - this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Switch.Service.setCharacteristic(this.hap.Characteristic.Name, SwitchService); // handle on / off events using the On characteristic - this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } async OnSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`); - this.On = value; - if (this.On) { + this.Switch.On = value; + if (this.Switch.On) { this.pushOnChanges(); } else { this.pushOffChanges(); @@ -68,8 +67,9 @@ export class Camera extends irdeviceBase { * Camera - "command" "channelSub" "default" = previous channel */ async pushOnChanges(): Promise { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${this.On},` + ` disablePushOn: ${this.disablePushOn}`); - if (this.On && !this.disablePushOn) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${this.Switch.On},` + + ` disablePushOn: ${this.disablePushOn}`); + if (this.Switch.On && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -83,9 +83,9 @@ export class Camera extends irdeviceBase { async pushOffChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.On},` + ` disablePushOff: ${this.disablePushOff}`, + `${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.Switch.On},` + ` disablePushOff: ${this.disablePushOff}`, ); - if (!this.On && !this.disablePushOff) { + if (!this.Switch.On && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -137,16 +137,16 @@ export class Camera extends irdeviceBase { async updateHomeKitCharacteristics(): Promise { // On - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.Switch.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Switch.On}`); } else { - this.accessory.context.On = this.On; - this.switchService?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.Switch.On; + this.Switch.Service?.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`); } } async apiError(e: any): Promise { - this.switchService.updateCharacteristic(this.hap.Characteristic.On, e); + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, e); } } diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 4e8545a3..42bed859 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -15,14 +15,13 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class Fan extends irdeviceBase { // Services - fanService!: Service; - - // Characteristic Values - Active!: CharacteristicValue; - SwingMode!: CharacteristicValue; - RotationSpeed!: CharacteristicValue; - ActiveIdentifier!: CharacteristicValue; - RotationDirection!: CharacteristicValue; + private Fan: { + Service: Service; + Active: CharacteristicValue; + SwingMode: CharacteristicValue; + RotationSpeed: CharacteristicValue; + RotationDirection: CharacteristicValue; + }; // Config minStep?: number; @@ -36,23 +35,25 @@ export class Fan extends irdeviceBase { ) { super(platform, accessory, device); - // default placeholders - if (this.Active === undefined) { - this.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Active = this.accessory.context.Active; - } + // Initialize Switch property + this.Fan = { + Service: accessory.getService(this.hap.Service.Lightbulb)!, + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, + RotationSpeed: accessory.context.RotationSpeed || 0, + RotationDirection: accessory.context.On || this.hap.Characteristic.RotationDirection.CLOCKWISE, + }; // get the Television service if it exists, otherwise create a new Television service // you can create multiple services for each accessory - const fanService = `${accessory.displayName} Fan`; - (this.fanService = accessory.getService(this.hap.Service.Fanv2) - || accessory.addService(this.hap.Service.Fanv2)), fanService; + const FanService = `${accessory.displayName} Fan`; + (this.Fan.Service = accessory.getService(this.hap.Service.Fanv2) + || accessory.addService(this.hap.Service.Fanv2)), FanService; - this.fanService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Fan.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the Active characteristic - this.fanService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.Fan.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); if (device.irfan?.rotation_speed) { if (device.irfan?.set_minStep) { @@ -71,7 +72,7 @@ export class Fan extends irdeviceBase { this.maxValue = 100; } // handle Rotation Speed events using the RotationSpeed characteristic - this.fanService + this.Fan.Service .getCharacteristic(this.hap.Characteristic.RotationSpeed) .setProps({ minStep: this.minStep, @@ -79,9 +80,9 @@ export class Fan extends irdeviceBase { maxValue: this.maxValue, }) .onSet(this.RotationSpeedSet.bind(this)); - } else if (this.fanService.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !device.irfan?.swing_mode) { - const characteristic = this.fanService.getCharacteristic(this.hap.Characteristic.RotationSpeed); - this.fanService.removeCharacteristic(characteristic); + } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !device.irfan?.swing_mode) { + const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed); + this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Rotation Speed Characteristic was removed.`); } else { // eslint-disable-next-line max-len @@ -93,10 +94,10 @@ export class Fan extends irdeviceBase { if (device.irfan?.swing_mode) { // handle Osolcation events using the SwingMode characteristic - this.fanService.getCharacteristic(this.hap.Characteristic.SwingMode).onSet(this.SwingModeSet.bind(this)); - } else if (this.fanService.testCharacteristic(this.hap.Characteristic.SwingMode) && !device.irfan?.swing_mode) { - const characteristic = this.fanService.getCharacteristic(this.hap.Characteristic.SwingMode); - this.fanService.removeCharacteristic(characteristic); + this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode).onSet(this.SwingModeSet.bind(this)); + } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.SwingMode) && !device.irfan?.swing_mode) { + const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode); + this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was removed.`); } else { // eslint-disable-next-line max-len @@ -109,38 +110,38 @@ export class Fan extends irdeviceBase { async SwingModeSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${value}`); - if (value > this.SwingMode) { - this.SwingMode = 1; + if (value > this.Fan.SwingMode) { + this.Fan.SwingMode = 1; await this.pushFanOnChanges(); await this.pushFanSwingChanges(); } else { - this.SwingMode = 0; + this.Fan.SwingMode = 0; await this.pushFanOnChanges(); await this.pushFanSwingChanges(); } - this.SwingMode = value; - this.accessory.context.SwingMode = this.SwingMode; + this.Fan.SwingMode = value; + this.accessory.context.SwingMode = this.Fan.SwingMode; } async RotationSpeedSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${value}`); - if (value > this.RotationSpeed) { - this.RotationSpeed = 1; + if (value > this.Fan.RotationSpeed) { + this.Fan.RotationSpeed = 1; this.pushFanSpeedUpChanges(); this.pushFanOnChanges(); } else { - this.RotationSpeed = 0; + this.Fan.RotationSpeed = 0; this.pushFanSpeedDownChanges(); } - this.RotationSpeed = value; - this.accessory.context.RotationSpeed = this.RotationSpeed; + this.Fan.RotationSpeed = value; + this.accessory.context.RotationSpeed = this.Fan.RotationSpeed; } async ActiveSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${value}`); - this.Active = value; - if (this.Active === this.hap.Characteristic.Active.ACTIVE) { + this.Fan.Active = value; + if (this.Fan.Active === this.hap.Characteristic.Active.ACTIVE) { this.pushFanOnChanges(); } else { this.pushFanOffChanges(); @@ -157,10 +158,9 @@ export class Fan extends irdeviceBase { * Fan - "command" "highSpeed" "default" = fan speed to high */ async pushFanOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushFanOnChanges Active: ${this.Active},` + ` disablePushOn: ${this.disablePushOn}`, - ); - if (this.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushFanOnChanges Active: ${this.Fan.Active},` + + ` disablePushOn: ${this.disablePushOn}`); + if (this.Fan.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -174,10 +174,10 @@ export class Fan extends irdeviceBase { async pushFanOffChanges(): Promise { this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges Active: ${this.Active},` + + `${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges Active: ${this.Fan.Active},` + ` disablePushOff: ${this.disablePushOff}`, ); - if (this.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { + if (this.Fan.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -255,32 +255,32 @@ export class Fan extends irdeviceBase { } async updateHomeKitCharacteristics(): Promise { - if (this.Active === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); + if (this.Fan.Active === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`); } else { - this.accessory.context.Active = this.Active; - this.fanService?.updateCharacteristic(this.hap.Characteristic.Active, this.Active); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + this.accessory.context.Active = this.Fan.Active; + this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`); } - if (this.SwingMode === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${this.SwingMode}`); + if (this.Fan.SwingMode === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`); } else { - this.accessory.context.SwingMode = this.SwingMode; - this.fanService?.updateCharacteristic(this.hap.Characteristic.SwingMode, this.SwingMode); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.SwingMode}`); + this.accessory.context.SwingMode = this.Fan.SwingMode; + this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`); } - if (this.RotationSpeed === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.RotationSpeed}`); + if (this.Fan.RotationSpeed === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`); } else { - this.accessory.context.RotationSpeed = this.RotationSpeed; - this.fanService?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.RotationSpeed); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.RotationSpeed}`); + this.accessory.context.RotationSpeed = this.Fan.RotationSpeed; + this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`); } } async apiError(e: any): Promise { - this.fanService.updateCharacteristic(this.hap.Characteristic.Active, e); - this.fanService.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); - this.fanService.updateCharacteristic(this.hap.Characteristic.SwingMode, e); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, e); } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index cf8a20b3..89544f8d 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -322,7 +322,7 @@ export abstract class irdeviceBase { async getDeviceContext(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): Promise { accessory.context.name = device.deviceName; - accessory.context.model = device.model; + accessory.context.model = device.remoteType; accessory.context.deviceId = device.deviceId; accessory.context.remoteType = device.remoteType; if (device.firmware === undefined) { diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 900eeddd..b44d59c5 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -15,7 +15,11 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class Light extends irdeviceBase { // Services - lightBulbService?: Service; + private LightBulb!: { + Service: Service; + On: CharacteristicValue; + }; + ProgrammableSwitchServiceOn?: Service; ProgrammableSwitchServiceOff?: Service; @@ -33,25 +37,23 @@ export class Light extends irdeviceBase { ) { super(platform, accessory, device); - // default placeholders - if (this.On === undefined) { - this.On = false; - } else { - this.On = accessory.context.On; - } - if (!device.irlight?.stateless) { + // Initialize LightBulb property + this.LightBulb = { + Service: accessory.getService(this.hap.Service.Lightbulb)!, + On: accessory.context.On || false, + }; // get the Light service if it exists, otherwise create a new Light service // you can create multiple services for each accessory - const lightBulbService = `${accessory.displayName} ${device.remoteType}`; - (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService; + const LightBulbService = `${accessory.displayName} ${device.remoteType}`; + (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) + || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // handle on / off events using the On characteristic - this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else { // create a new Stateful Programmable Switch On service @@ -252,7 +254,7 @@ export class Light extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); } else { this.accessory.context.On = this.On; - this.lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, this.On); + this.LightBulb.Service?.updateCharacteristic(this.hap.Characteristic.On, this.On); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } } else { @@ -283,7 +285,7 @@ export class Light extends irdeviceBase { async apiError(e: any): Promise { if (this.device.irlight?.stateless) { - this.lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, e); + this.LightBulb.Service?.updateCharacteristic(this.hap.Characteristic.On, e); } else { this.ProgrammableSwitchServiceOn?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); this.ProgrammableSwitchServiceOn?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index b780f8cc..24d73c08 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -14,7 +14,7 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; * Each accessory may expose multiple services of different service types. */ export class Others extends irdeviceBase { - + // Services private Switch?: { Service: Service; On: CharacteristicValue; @@ -82,8 +82,8 @@ export class Others extends irdeviceBase { if (this.otherDeviceType === 'switch') { // Initialize Switch property this.Switch = { - Service: accessory.addService(this.hap.Service.Switch), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Switch)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -106,8 +106,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'garagedoor') { // Initialize Garage Door property this.GarageDoor = { - Service: accessory.addService(this.hap.Service.GarageDoorOpener), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.GarageDoorOpener)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -131,8 +131,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'door') { // Initialize Door property this.Door = { - Service: accessory.addService(this.hap.Service.Door), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Door)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -164,8 +164,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'window') { // Initialize Window property this.Window = { - Service: accessory.addService(this.hap.Service.Window), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Window)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -197,8 +197,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'windowcovering') { // Initialize WindowCovering property this.WindowCovering = { - Service: accessory.addService(this.hap.Service.WindowCovering), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.WindowCovering)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -230,8 +230,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'lock') { // Initialize Lock property this.Lock = { - Service: accessory.addService(this.hap.Service.LockMechanism), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.LockMechanism)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeDoorService(accessory); @@ -254,8 +254,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'faucet') { // Initialize Faucet property this.Faucet = { - Service: accessory.addService(this.hap.Service.Faucet), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Faucet)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -278,8 +278,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'fan') { // Initialize Fan property this.Fan = { - Service: accessory.addService(this.hap.Service.Fanv2), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Fanv2)!, + On: accessory.context.On || false, }; this.removeLockService(accessory); this.removeDoorService(accessory); @@ -302,8 +302,8 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'stateful') { // Initialize StatefulProgrammableSwitch property this.StatefulProgrammableSwitch = { - Service: accessory.addService(this.hap.Service.StatefulProgrammableSwitch), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -328,8 +328,8 @@ export class Others extends irdeviceBase { } else { // Initialize Outlet property this.Outlet = { - Service: accessory.addService(this.hap.Service.Outlet), - On: accessory.context.On || true, + Service: accessory.getService(this.hap.Service.Outlet)!, + On: accessory.context.On || false, }; this.removeFanService(accessory); this.removeLockService(accessory); @@ -700,92 +700,92 @@ export class Others extends irdeviceBase { async removeOutletService(accessory: PlatformAccessory): Promise { // If Outlet.Service still present, then remove first + if (this.Outlet?.Service) { this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; - if (this.Outlet!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); accessory.removeService(this.Outlet!.Service); + } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If GarageDoor.Service still present, then remove first + if (this.GarageDoor?.Service) { this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; - if (this.GarageDoor!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); accessory.removeService(this.GarageDoor!.Service); + } } async removeDoorService(accessory: PlatformAccessory): Promise { // If Door.Service still present, then remove first + if (this.Door?.Service) { this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; - if (this.Door!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); accessory.removeService(this.Door!.Service); + } } async removeLockService(accessory: PlatformAccessory): Promise { // If Lock.Service still present, then remove first + if (this.Lock?.Service) { this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; - if (this.Lock!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); accessory.removeService(this.Lock!.Service); + } } async removeFaucetService(accessory: PlatformAccessory): Promise { // If Faucet.Service still present, then remove first + if (this.Faucet?.Service) { this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; - if (this.Faucet!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); accessory.removeService(this.Faucet!.Service); + } } async removeFanService(accessory: PlatformAccessory): Promise { // If Fan Service still present, then remove first + if (this.Fan?.Service) { this.Fan!.Service = this.accessory.getService(this.hap.Service.Fan) as Service; - if (this.Fan!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); accessory.removeService(this.Fan!.Service); + } } async removeWindowService(accessory: PlatformAccessory): Promise { // If Window.Service still present, then remove first + if (this.Window?.Service) { this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; - if (this.Window!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); accessory.removeService(this.Window!.Service); + } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If WindowCovering.Service still present, then remove first + if (this.WindowCovering?.Service) { this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; - if (this.WindowCovering!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); accessory.removeService(this.WindowCovering!.Service); + } } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If StatefulProgrammableSwitch.Service still present, then remove first + if (this.StatefulProgrammableSwitch?.Service) { this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; - if (this.StatefulProgrammableSwitch!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); accessory.removeService(this.StatefulProgrammableSwitch!.Service); + } } async removeSwitchService(accessory: PlatformAccessory): Promise { // If Switch.Service still present, then remove first + if (this.Switch?.Service) { this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; - if (this.Switch!.Service) { - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); - } + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); accessory.removeService(this.Switch!.Service); + } } async getOtherConfigSettings(device: irdevice & irDevicesConfig): Promise { diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index bb87a608..286283df 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -41,7 +41,7 @@ export class TV extends irdeviceBase { // Initialize Television property this.Television = { - Service: accessory.addService(this.hap.Service.Television), + Service: accessory.getService(this.hap.Service.Television)!, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, ActiveIdentifier: accessory.context.ActiveIdentifier || 1, SleepDiscoveryMode: accessory.context.SleepDiscoveryMode || this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, @@ -50,7 +50,7 @@ export class TV extends irdeviceBase { // Initialize TelevisionSpeaker property this.TelevisionSpeaker = { - Service: accessory.addService(this.hap.Service.TelevisionSpeaker), + Service: accessory.getService(this.hap.Service.TelevisionSpeaker)!, Active: accessory.context.Active || false, VolumeControlType: accessory.context.VolumeControlType || this.hap.Characteristic.VolumeControlType.ABSOLUTE, VolumeSelector: accessory.context.VolumeSelector || this.hap.Characteristic.VolumeSelector.INCREMENT, diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index bf9e1345..8c4071b9 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -29,7 +29,7 @@ export class VacuumCleaner extends irdeviceBase { // Initialize Switch property this.Switch = { - Service: accessory.addService(this.hap.Service.Switch), + Service: accessory.getService(this.hap.Service.Switch)!, On: accessory.context.On || false, }; diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index f10d73d2..a10d3a0b 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -29,7 +29,7 @@ export class WaterHeater extends irdeviceBase { // Initialize Valve property this.Valve = { - Service: accessory.addService(this.hap.Service.Valve), + Service: accessory.getService(this.hap.Service.Valve)!, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, }; diff --git a/src/platform.ts b/src/platform.ts index fe8ddb51..0051835c 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -39,12 +39,10 @@ import fakegato from 'fakegato-history'; import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; -import { EveHomeKitTypes } from 'homebridge-lib'; +import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes'; import { UrlObject } from 'url'; import { sleep } from './utils.js'; - - /** * HomebridgePlatform * This class is the main constructor for your plugin, this is where you should @@ -387,7 +385,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } if (!this.config.options.maxRetries) { - this.config.options!.maxRetries! = 5; + this.config.options.maxRetries = 5; this.debugWarnLog('Using Default Max Retries.'); } else { this.maxRetries = this.config.options.maxRetries; @@ -513,6 +511,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { let retryCount = 0; const maxRetries = this.maxRetries; // Maximum number of retries const delayBetweenRetries = this.delayBetweenRetries; // Delay between retries in milliseconds + this.debugWarnLog(`Retry Count: ${retryCount}`); + this.debugWarnLog(`Max Retries: ${maxRetries}`); + this.debugWarnLog(`Delay Between Retries: ${delayBetweenRetries}`); while (retryCount < maxRetries) { try { const { body, statusCode } = await request(Devices, { @@ -2718,6 +2719,22 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (this.config.options.pushRate) { platformConfig.pushRate = this.config.options.pushRate; } + if (this.config.options.maxRetries) { + this.maxRetries = this.config.options.maxRetries; + platformConfig.maxRetries = this.config.options.maxRetries; + } else { + this.maxRetries = 3; + this.debugWarnLog('Using Default Max Retries'); + platformConfig.maxRetries = this.maxRetries; + } + if (this.config.options.delayBetweenRetries) { + this.delayBetweenRetries = this.config.options.delayBetweenRetries * 1000; + platformConfig.delayBetweenRetries = this.config.options.delayBetweenRetries; + } else { + this.delayBetweenRetries = 3000; + this.debugWarnLog('Using Default Delay Between Retries'); + platformConfig.delayBetweenRetries = this.delayBetweenRetries / 1000; + } if (Object.entries(platformConfig).length !== 0) { this.debugLog(`Platform Config: ${JSON.stringify(platformConfig)}`); } From 774969684d7f789e577981f7c4f29f565542ec8d Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Wed, 15 May 2024 13:24:27 -0500 Subject: [PATCH 30/73] Error logs to Success logs --- src/device/blindtilt.ts | 4 ++-- src/device/bot.ts | 4 ++-- src/device/ceilinglight.ts | 10 +++++----- src/device/colorbulb.ts | 2 +- src/device/contact.ts | 2 +- src/device/curtain.ts | 2 +- src/device/device.ts | 14 +++++++++++--- src/device/hub.ts | 2 +- src/device/humidifier.ts | 2 +- src/device/iosensor.ts | 2 +- src/device/lightstrip.ts | 2 +- src/device/lock.ts | 2 +- src/device/meter.ts | 2 +- src/device/meterplus.ts | 2 +- src/device/motion.ts | 2 +- src/device/plug.ts | 2 +- src/device/robotvacuumcleaner.ts | 4 ++-- src/device/waterdetector.ts | 2 +- src/irdevice/airconditioner.ts | 2 +- src/irdevice/airpurifier.ts | 2 +- src/irdevice/camera.ts | 2 +- src/irdevice/fan.ts | 2 +- src/irdevice/irdevice.ts | 12 ++++++++++-- src/irdevice/light.ts | 2 +- src/irdevice/other.ts | 2 +- src/irdevice/tv.ts | 2 +- src/irdevice/vacuumcleaner.ts | 2 +- src/irdevice/waterheater.ts | 2 +- 28 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index fcf7913a..26cfc435 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -502,7 +502,7 @@ export class BlindTilt extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); @@ -634,7 +634,7 @@ export class BlindTilt extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${bodyChange} sent successfully`); diff --git a/src/device/bot.ts b/src/device/bot.ts index 560795fa..3985124a 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -588,7 +588,7 @@ export class Bot extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); @@ -754,7 +754,7 @@ export class Bot extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${bodyChange} sent successfully`); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 8b3364f9..dcdf2359 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -336,7 +336,7 @@ export class CeilingLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); @@ -465,7 +465,7 @@ export class CeilingLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${bodyChange} sent successfully`); @@ -526,7 +526,7 @@ export class CeilingLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${bodyChange} sent successfully`); @@ -572,7 +572,7 @@ export class CeilingLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); } else { this.statusCode(statusCode); @@ -614,7 +614,7 @@ export class CeilingLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${bodyChange} sent successfully`); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 94a87fc3..cd8da835 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -404,7 +404,7 @@ export class ColorBulb extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/contact.ts b/src/device/contact.ts index 363cb568..890b2712 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -348,7 +348,7 @@ export class Contact extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 4e33eaef..5292bf57 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -530,7 +530,7 @@ export class Curtain extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/device.ts b/src/device/device.ts index a07b97b8..b4153016 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -599,15 +599,23 @@ export abstract class deviceBase { /** * Logging for Device */ + infoLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.info(String(...log)); + } + } + successLog(...log: any[]): void { if (this.enablingDeviceLogging()) { - this.platform.log.success(String(...log)); + this.log.success(String(...log)); } } - infoLog(...log: any[]): void { + debugSuccessLog(...log: any[]): void { if (this.enablingDeviceLogging()) { - this.log.info(String(...log)); + if (this.deviceLogging?.includes('debug')) { + this.log.success('[DEBUG]', String(...log)); + } } } diff --git a/src/device/hub.ts b/src/device/hub.ts index 5d5f6c68..552c666c 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -240,7 +240,7 @@ export class Hub extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 458a370b..29d682ae 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -335,7 +335,7 @@ export class Humidifier extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index e3c0fe1b..5116569a 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -294,7 +294,7 @@ export class IOSensor extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index c396e3ff..251a969d 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -366,7 +366,7 @@ export class StripLight extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/lock.ts b/src/device/lock.ts index 5f8f98f7..162e7619 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -325,7 +325,7 @@ export class Lock extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/meter.ts b/src/device/meter.ts index 5da1950a..18cac154 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -290,7 +290,7 @@ export class Meter extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index bb09e095..47b815a4 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -294,7 +294,7 @@ export class MeterPlus extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/motion.ts b/src/device/motion.ts index 18f33627..0d3d551b 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -269,7 +269,7 @@ export class Motion extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/plug.ts b/src/device/plug.ts index d69cab07..02e51dd8 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -199,7 +199,7 @@ export class Plug extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index ce69f979..0fabfc77 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -317,7 +317,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); @@ -471,7 +471,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index cdc2c4d0..4e4fa747 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -230,7 +230,7 @@ export class WaterDetector extends deviceBase { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index 83f79338..830f8908 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -346,7 +346,7 @@ export class AirConditioner extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 1cadd0d7..cfa760d5 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -222,7 +222,7 @@ export class AirPurifier extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index f922aabb..3d22dad1 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -112,7 +112,7 @@ export class Camera extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 42bed859..3b518eeb 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -231,7 +231,7 @@ export class Fan extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 89544f8d..b494c5c8 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -462,15 +462,23 @@ export abstract class irdeviceBase { /** * Logging for Device */ + infoLog(...log: any[]): void { + if (this.enablingDeviceLogging()) { + this.log.info(String(...log)); + } + } + successLog(...log: any[]): void { if (this.enablingDeviceLogging()) { this.platform.log.success(String(...log)); } } - infoLog(...log: any[]): void { + debugSuccessLog(...log: any[]): void { if (this.enablingDeviceLogging()) { - this.log.info(String(...log)); + if (this.deviceLogging?.includes('debug')) { + this.log.success('[DEBUG]', String(...log)); + } } } diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index b44d59c5..3c58e7af 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -223,7 +223,7 @@ export class Light extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.accessory.context.On = this.On; diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 24d73c08..639bcad7 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -491,7 +491,7 @@ export class Others extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 286283df..0d5ef88f 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -388,7 +388,7 @@ export class TV extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 8c4071b9..923c491d 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -107,7 +107,7 @@ export class VacuumCleaner extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index a10d3a0b..fc1e54a9 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -112,7 +112,7 @@ export class WaterHeater extends irdeviceBase { this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); this.debugWarnLog(`${this.device.remoteType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { - this.debugErrorLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); this.updateHomeKitCharacteristics(); From caf4d84886541495f6bb6914e7c54200ef2debe2 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:04:23 -0500 Subject: [PATCH 31/73] more changes --- CHANGELOG.md | 1 + config.schema.json | 68 +++- package-lock.json | 32 +- package.json | 4 +- src/device/blindtilt.ts | 11 +- src/device/bot.ts | 11 +- src/device/ceilinglight.ts | 36 +- src/device/colorbulb.ts | 31 +- src/device/contact.ts | 13 +- src/device/curtain.ts | 37 ++- src/device/device.ts | 16 +- src/device/fan.ts | 548 +++++++++++++++++++++++++++++++ src/device/hub.ts | 52 +-- src/device/humidifier.ts | 11 +- src/device/iosensor.ts | 46 +-- src/device/lightstrip.ts | 46 ++- src/device/lock.ts | 19 +- src/device/meter.ts | 50 +-- src/device/meterplus.ts | 17 +- src/device/motion.ts | 11 +- src/device/plug.ts | 11 +- src/device/robotvacuumcleaner.ts | 21 +- src/device/waterdetector.ts | 21 +- src/irdevice/airconditioner.ts | 7 +- src/irdevice/airpurifier.ts | 7 +- src/irdevice/camera.ts | 5 +- src/irdevice/fan.ts | 5 +- src/irdevice/irdevice.ts | 2 +- src/irdevice/light.ts | 3 +- src/irdevice/other.ts | 91 ++--- src/irdevice/tv.ts | 3 +- src/irdevice/vacuumcleaner.ts | 3 +- src/irdevice/waterheater.ts | 3 +- src/platform.ts | 76 ++++- src/settings.ts | 43 ++- src/utils.ts | 33 +- 36 files changed, 1153 insertions(+), 241 deletions(-) create mode 100644 src/device/fan.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 827860ab..f9f6dfaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed - Add Support for `Water Detector` +- Add Support for `Battery Circulator Fan` - Add BLE support for `Smart Lock` - Add `K10+` deviceType Support - Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev) diff --git a/config.schema.json b/config.schema.json index 9b1a9240..ea1c47a6 100644 --- a/config.schema.json +++ b/config.schema.json @@ -308,9 +308,30 @@ "title": "Hide Hub 2's Temperature Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2') && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Hub 2' && model.options.devices[arrayIndices].deviceId);" } }, + "convertUnitTo": { + "title": "Convert Hub's Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Hub 2' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hub.hide_temperature);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, "hide_humidity": { "title": "Hide Hub 2's Humidity Sensor", "type": "boolean", @@ -481,6 +502,27 @@ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" } }, + "convertUnitTo": { + "title": "Convert Meter's Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].meter.hide_temperature);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, "hide_humidity": { "title": "Hide Meter's Humidity Sensor", "type": "boolean", @@ -500,6 +542,27 @@ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId);" } }, + "convertUnitTo": { + "title": "Convert Indoor/Outdoor Sensor's Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].iosensor.hide_temperature);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, "hide_humidity": { "title": "Hide Indoor/Outdoor's Humidity Sensor", "type": "boolean", @@ -1670,6 +1733,7 @@ "options.devices[].connectionType", "options.devices[].webhook", "options.devices[].hub.hide_temperature", + "options.devices[].hub.convertUnitTo", "options.devices[].hub.hide_humidity", "options.devices[].hub.hide_lightsensor", "options.devices[].scanDuration", @@ -1683,8 +1747,10 @@ "options.devices[].bot.doublePress", "options.devices[].bot.pushRatePress", "options.devices[].meter.hide_temperature", + "options.devices[].meter.convertUnitTo", "options.devices[].meter.hide_humidity", "options.devices[].iosensor.hide_temperature", + "options.devices[].iosensor.convertUnitTo", "options.devices[].iosensor.hide_humidity", "options.devices[].waterdetector.hide_leak", "options.devices[].humidifier.set_minStep", diff --git a/package-lock.json b/package-lock.json index bd48f8f4..a7d7f18d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@typescript-eslint/eslint-plugin": "^7.9.0", "@typescript-eslint/parser": "^7.9.0", "eslint": "^8.57.0", - "homebridge": "^1.8.1", + "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", @@ -40,7 +40,7 @@ "typescript": "^5.4.5" }, "engines": { - "homebridge": "^1.8.1", + "homebridge": "^1.8.2", "node": "^18 || ^20 || ^22" }, "optionalDependencies": { @@ -5061,9 +5061,9 @@ } }, "node_modules/hap-nodejs": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.12.0.tgz", - "integrity": "sha512-W+KPE4kCtudt/WTEHlXLiZmgIC5IgK8TXpUPmp27Qm7TOfKwWVGhKYXWRBEr3bkgSe3CGuGQN8vFeRB//mpfhQ==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.12.1.tgz", + "integrity": "sha512-iUUMaK6ucDKLMjT4m5Oz6CoLKkGg+omI6GR96weyL8fPGR1HYoCMtoJoUNW2NSIp4b2A6hx4zjNOEtLEaTA2MQ==", "dev": true, "dependencies": { "@homebridge/ciao": "^1.2.0", @@ -5285,17 +5285,17 @@ } }, "node_modules/homebridge": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.1.tgz", - "integrity": "sha512-tcyjT79m1SxBHf3bQyI9IEsXN0m1y0GPxPWvwhdJEycdliK0OyuYxzx4w1M5c7PHZknkZbQWCxFyaMTh7DHKUA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.2.tgz", + "integrity": "sha512-K0P9/qk3RdAKGLhGrmtF4skUjcygNlnBu0S/ssKIdp4p0kMzW2wjw2Q+z7TCxgZVy84/kaR09UD1n6uJAunTOQ==", "dev": true, "dependencies": { "chalk": "4.1.2", "commander": "12.0.0", "fs-extra": "11.2.0", - "hap-nodejs": "0.12.0", + "hap-nodejs": "0.12.1", "qrcode-terminal": "0.12.0", - "semver": "7.6.0", + "semver": "7.6.2", "source-map-support": "0.5.21" }, "bin": { @@ -5390,6 +5390,18 @@ "node": "20.13.1||^20||^18" } }, + "node_modules/homebridge/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/hosted-git-info": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", diff --git a/package.json b/package.json index 3300b103..731dda3c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "engineStrict": true, "engines": { - "homebridge": "^1.8.1", + "homebridge": "^1.8.2", "node": "^18 || ^20 || ^22" }, "main": "dist/index.js", @@ -92,7 +92,7 @@ "@typescript-eslint/eslint-plugin": "^7.9.0", "@typescript-eslint/parser": "^7.9.0", "eslint": "^8.57.0", - "homebridge": "^1.8.1", + "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 26cfc435..f3a74e09 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -164,8 +164,8 @@ export class BlindTilt extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -437,6 +437,11 @@ export class BlindTilt extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } async refreshStatus(): Promise { @@ -637,7 +642,7 @@ export class BlindTilt extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/bot.ts b/src/device/bot.ts index 3985124a..82986620 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -400,8 +400,8 @@ export class Bot extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -520,6 +520,11 @@ export class Bot extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -757,7 +762,7 @@ export class Bot extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index dcdf2359..b75844cc 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -154,8 +154,8 @@ export class CeilingLight extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -165,9 +165,30 @@ export class CeilingLight extends deviceBase { '(powerState, brightness, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${ColorTemperature})`); + + // On this.LightBulb.On = powerState === 'ON' ? true : false; + if (this.accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness this.LightBulb.Brightness = brightness; + if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } + + // ColorTemperature this.LightBulb.ColorTemperature = colorTemperature; + if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -268,6 +289,11 @@ export class CeilingLight extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -468,7 +494,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -529,7 +555,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -617,7 +643,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index cd8da835..bd1ec82b 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -157,8 +157,8 @@ export class ColorBulb extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -168,8 +168,22 @@ export class ColorBulb extends deviceBase { '(powerState, brightness, color, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); + + // On this.LightBulb.On = powerState === 'ON' ? true : false; + if (this.accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness this.LightBulb.Brightness = brightness; + if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); const [red, green, blue] = color!.split(':'); @@ -200,7 +214,7 @@ export class ColorBulb extends deviceBase { this.LightBulb.ColorTemperature = colorTemperature; if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } @@ -336,6 +350,11 @@ export class ColorBulb extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -672,7 +691,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -787,11 +806,11 @@ export class ColorBulb extends deviceBase { } catch (e: any) { this.apiError(e); this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` - + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` - + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); } } diff --git a/src/device/contact.ts b/src/device/contact.ts index 890b2712..d9af719a 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -136,8 +136,8 @@ export class Contact extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -280,6 +280,11 @@ export class Contact extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -454,10 +459,10 @@ export class Contact extends deviceBase { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED); if (!this.device.contact?.hide_motionsensor) { - this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); + this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); } if (!this.device.contact?.hide_lightsensor) { - this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, 100); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, 100); } } } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 5292bf57..7cc933d2 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -154,8 +154,8 @@ export class Curtain extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -408,20 +408,20 @@ export class Curtain extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING` - + ` PositionState: ${this.WindowCovering.PositionState}`); + + ` PositionState: ${this.WindowCovering.PositionState}`); } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `); this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` - + ` PositionState: ${this.WindowCovering.PositionState}`); + + ` PositionState: ${this.WindowCovering.PositionState}`); } else { this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` - + ` PositionState: ${this.WindowCovering.PositionState}`); + + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`); @@ -447,7 +447,7 @@ export class Curtain extends deviceBase { this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // BatteryLevel @@ -465,6 +465,11 @@ export class Curtain extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } async refreshStatus(): Promise { @@ -704,17 +709,17 @@ export class Curtain extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; this.setNewTarget = true; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else if (value < this.WindowCovering.CurrentPosition) { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; this.setNewTarget = true; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.setNewTarget = false; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); @@ -751,7 +756,7 @@ export class Curtain extends deviceBase { this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition; this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); if (this.device.mqttURL) { this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); // Convert to string } @@ -765,7 +770,7 @@ export class Curtain extends deviceBase { this.accessory.context.PositionState = this.WindowCovering.PositionState; this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` PositionState: ${this.WindowCovering.PositionState}`); + + ` PositionState: ${this.WindowCovering.PositionState}`); } if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`); @@ -776,7 +781,7 @@ export class Curtain extends deviceBase { this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition; this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: ${this.WindowCovering.TargetPosition}`); + + ` TargetPosition: ${this.WindowCovering.TargetPosition}`); } if (this.WindowCovering.HoldPosition === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.WindowCovering.HoldPosition}`); @@ -787,12 +792,12 @@ export class Curtain extends deviceBase { this.accessory.context.HoldPosition = this.WindowCovering.HoldPosition; this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` HoldPosition: ${this.WindowCovering.HoldPosition}`); + + ` HoldPosition: ${this.WindowCovering.HoldPosition}`); } if (!this.device.curtain?.hide_lightsensor) { if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { if (this.device.mqttURL) { this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString()); @@ -830,7 +835,7 @@ export class Curtain extends deviceBase { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } diff --git a/src/device/device.ts b/src/device/device.ts index b4153016..9dd2050c 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -195,13 +195,13 @@ export abstract class deviceBase { if (device.offline !== undefined) { config['offline'] = device.offline; } - if (device.maxRetry !== undefined) { + if (device.maxRetry !== 0 ) { config['maxRetry'] = device.maxRetry; } - if (device.webhook !== undefined) { + if (device.webhook === true) { config['webhook'] = device.webhook; } - if (device.connectionType !== undefined) { + if (device.connectionType) { config['connectionType'] = device.connectionType; } if (device.external !== undefined) { @@ -479,6 +479,11 @@ export abstract class deviceBase { device.bleModel = SwitchBotBLEModel.Unknown; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; + case 'Robot Vacuum Cleaner S10': + device.model = SwitchBotModel.RobotVacuumCleanerS10; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; case 'Ceiling Light': device.model = SwitchBotModel.CeilingLight; device.bleModel = SwitchBotBLEModel.CeilingLight; @@ -509,6 +514,11 @@ export abstract class deviceBase { device.bleModel = SwitchBotBLEModel.Unknown; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; + case 'Battery Circulator Fan': + device.model = SwitchBotModel.BatteryCirculatorFan; + device.bleModel = SwitchBotBLEModel.Unknown; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + break; default: device.model = SwitchBotModel.Unknown; device.bleModel = SwitchBotBLEModel.Unknown; diff --git a/src/device/fan.ts b/src/device/fan.ts new file mode 100644 index 00000000..1ebf6901 --- /dev/null +++ b/src/device/fan.ts @@ -0,0 +1,548 @@ +/* eslint-disable max-len */ +/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * plug.ts: @switchbot/homebridge-switchbot. + */ +import { request } from 'undici'; +import { deviceBase } from './device.js'; +import { SwitchBotPlatform } from '../platform.js'; +import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; +import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +export class Fan extends deviceBase { + // Services + private Fan: { + Service: Service; + Active: CharacteristicValue; + SwingMode: CharacteristicValue; + RotationSpeed: CharacteristicValue; + }; + + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + ChargingState: CharacteristicValue; + }; + + // Updates + plugUpdateInProgress!: boolean; + doPlugUpdate!: Subject; + + constructor( + readonly platform: SwitchBotPlatform, + accessory: PlatformAccessory, + device: device & devicesConfig, + ) { + super(platform, accessory, device); + // this is subject we use to track when we need to POST changes to the SwitchBot API + this.doPlugUpdate = new Subject(); + this.plugUpdateInProgress = false; + + // Initialize Fan property + this.Fan = { + Service: accessory.getService(this.hap.Service.Fanv2)!, + Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, + RotationSpeed: accessory.context.RotationSpeed || 0, + }; + + // Initialize Battery property + this.Battery = { + Service: accessory.getService(this.hap.Service.Battery)!, + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGING, + }; + + // Retrieve initial values and updateHomekit + this.refreshStatus(); + + // get the Fan service if it exists, otherwise create a new Fanv2 service + // you can create multiple services for each accessory + const FanService = `${accessory.displayName} ${device.deviceType}`; + (this.Fan.Service = accessory.getService(this.hap.Service.Fanv2) + || accessory.addService(this.hap.Service.Fanv2)), FanService; + + this.Fan.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + // each service must implement at-minimum the "required characteristics" for the given service type + // see https://developers.homebridge.io/#/service/Fanv2 + + // create handlers for required characteristics + this.Fan.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + // create handlers for required characteristics + this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed).onSet(this.RotationSpeedSet.bind(this)); + // create handlers for required characteristics + this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode).onSet(this.SwingModeSet.bind(this)); + + // Update Homekit + this.updateHomeKitCharacteristics(); + + // Start an update interval + interval(this.deviceRefreshRate * 1000) + .pipe(skipWhile(() => this.plugUpdateInProgress)) + .subscribe(async () => { + await this.refreshStatus(); + }); + + //regisiter webhook event handler + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { + try { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { version, battery, powerState, oscillation, chargingStatus, fanSpeed } = context; + const { Active, SwingMode, RotationSpeed } = this.Fan; + const { BatteryLevel, ChargingState } = this.Battery; + const { FirmwareRevision } = this.accessory.context; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(version, battery, powerState, oscillation, chargingStatus, fanSpeed) = ' + + `Webhook:(${version}, ${battery}, ${powerState}, ${oscillation}, ${chargingStatus}, ${fanSpeed}), ` + + `current:(${FirmwareRevision}, ${BatteryLevel}, ${Active}, ${SwingMode}, ${ChargingState}, ${RotationSpeed})`); + + // Active + this.Fan.Active = powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE; + + // SwingMode + this.Fan.SwingMode = oscillation === 'on' ? + this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED; + + // RotationSpeed + this.Fan.RotationSpeed = fanSpeed; + + // ChargingState + this.Battery.ChargingState = chargingStatus === 'charging' ? + this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; + + // BatteryLevel + this.Battery.BatteryLevel = battery; + + // FirmwareRevision + this.accessory.context.FirmwareRevision = version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } + + // Watch for Plug change events + // We put in a debounce of 100ms so we don't make duplicate calls + this.doPlugUpdate + .pipe( + tap(() => { + this.plugUpdateInProgress = true; + }), + debounceTime(this.platform.config.options!.pushRate! * 1000), + ) + .subscribe(async () => { + try { + await this.pushChanges(); + } catch (e: any) { + this.apiError(e); + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`, + ); + } + this.plugUpdateInProgress = false; + }); + } + + async BLEparseStatus(serviceData: serviceData): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`); + + // State + switch (serviceData.state) { + case 'on': + this.Fan.Active = true; + break; + default: + this.Fan.Active = false; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`); + } + + async openAPIparseStatus(deviceStatus: deviceStatus) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`); + + // Active + this.Fan.Active = deviceStatus.body.power === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`); + + // SwingMode + this.Fan.SwingMode = deviceStatus.body.oscillation === 'on' ? + this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED; + + // RotationSpeed + this.Fan.RotationSpeed = deviceStatus.body.fanSpeed; + + // ChargingState + this.Battery.ChargingState = deviceStatus.body.chargingStatus === 'charging' ? + this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; + + // BatteryLevel + this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + if (this.Battery.BatteryLevel < 10) { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; + } else { + this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; + } + if (Number.isNaN(this.Battery.BatteryLevel)) { + this.Battery.BatteryLevel = 100; + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + // FirmwareRevision + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } + + + + /** + * Asks the SwitchBot API for the latest device information + */ + async refreshStatus(): Promise { + if (!this.device.enableCloudService && this.OpenAPI) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`); + } else if (this.BLE) { + await this.BLERefreshStatus(); + } else if (this.OpenAPI && this.platform.config.credentials?.token) { + await this.openAPIRefreshStatus(); + } else { + await this.offlineOff(); + this.debugWarnLog( + `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`, + ); + } + } + + async BLERefreshStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`); + const switchbot = await this.platform.connectBLE(); + // Convert to BLE Address + this.device.bleMac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + this.getCustomBLEAddress(switchbot); + // Start to monitor advertisement packets + (async () => { + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); + // Set an event handler + switchbot.onadvertisement = (ad: any) => { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + } + }; + // Wait 10 seconds + await switchbot.wait(this.scanDuration * 1000); + // Stop to monitor + await switchbot.stopScan(); + // Update HomeKit + await this.BLEparseStatus(switchbot.onadvertisement.serviceData); + await this.updateHomeKitCharacteristics(); + })(); + if (switchbot === undefined) { + await this.BLERefreshConnection(switchbot); + } + } + + async openAPIRefreshStatus(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); + try { + const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); + const deviceStatus: any = await body.json(); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.openAPIparseStatus(deviceStatus); + this.updateHomeKitCharacteristics(); + } else { + this.statusCode(statusCode); + this.statusCode(deviceStatus.statusCode); + } + } catch (e: any) { + this.apiError(e); + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`, + ); + } + } + + /** + * Pushes the requested changes to the SwitchBot API + * deviceType commandType Command command parameter Description + * Battery Circulator Fan - "command" "turnOff" "default" = set to OFF state + * Battery Circulator Fan - "command" "turnOn" "default" = set to ON state + * Battery Circulator Fan - "command" "setNightLightMode" "off, 1, or 2" = off, turn off nightlight, (1, bright) (2, dim) + * Battery Circulator Fan - "command" "setWindMode" "direct, natural, sleep, or baby" = Set fan mode. direct: direct mode. natural: natural mode. sleep: sleep mode. baby: ultra quiet mode + * Battery Circulator Fan - "command" "setWindSpeed" "{1-100} e.g. 10" = Set fan speed.1~100 + */ + + async pushChanges(): Promise { + if (!this.device.enableCloudService && this.OpenAPI) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`); + } else if (this.BLE) { + await this.BLEpushChanges(); + } else if (this.OpenAPI && this.platform.config.credentials?.token) { + await this.openAPIpushChanges(); + } else { + await this.offlineOff(); + this.debugWarnLog( + `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, + ); + } + // Refresh the status from the API + interval(15000) + .pipe(skipWhile(() => this.plugUpdateInProgress)) + .pipe(take(1)) + .subscribe(async () => { + await this.refreshStatus(); + }); + } + + async BLEpushChanges(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); + if (this.Fan.Active !== this.accessory.context.Active) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` + + ` On: ${this.Fan.Active} OnCached: ${this.accessory.context.Active}`); + const switchbot = await this.platform.connectBLE(); + // Convert to BLE Address + this.device.bleMac = this.device + .deviceId!.match(/.{1,2}/g)! + .join(':') + .toLowerCase(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + switchbot + .discover({ + model: this.device.bleModel, + id: this.device.bleMac, + }) + .then(async (device_list: any) => { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`); + return await this.retryBLE({ + max: await this.maxRetryBLE(), + fn: async () => { + if (this.Fan.Active) { + return await device_list[0].turnOn({ id: this.device.bleMac }); + } else { + return await device_list[0].turnOff({ id: this.device.bleMac }); + } + }, + }); + }) + .then(() => { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `Active: ${this.Fan.Active} sent over BLE, sent successfully`); + this.Fan.Active = false; + }) + .catch(async (e: any) => { + this.apiError(e); + this.errorLog( + `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`, + ); + await this.BLEPushConnection(); + }); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` + + ` Active: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`); + } + } + + async openAPIpushChanges() { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`); + if (this.Fan.Active !== this.accessory.context.Active) { + let command = ''; + if (this.Fan.Active) { + command = 'turnOn'; + } else { + command = 'turnOff'; + } + const bodyChange = JSON.stringify({ + command: `${command}`, + parameter: 'default', + commandType: 'command', + }); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); + try { + const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { + body: bodyChange, + method: 'POST', + headers: this.platform.generateHeaders(), + }); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); + const deviceStatus: any = await body.json(); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + } else { + this.statusCode(statusCode); + this.statusCode(deviceStatus.statusCode); + } + } catch (e: any) { + this.apiError(e); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + + `On: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`); + } + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + async ActiveSet(value: CharacteristicValue): Promise { + if (this.Fan.Active === this.accessory.context.Active) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Active: ${value}`); + } else { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); + } + + this.Fan.Active = value; + this.doPlugUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + async RotationSpeedSet(value: CharacteristicValue): Promise { + if (this.Fan.RotationSpeed === this.accessory.context.RotationSpeed) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set RotationSpeed: ${value}`); + } else { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RotationSpeed: ${value}`); + } + + this.Fan.RotationSpeed = value; + this.doPlugUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + async SwingModeSet(value: CharacteristicValue): Promise { + if (this.Fan.SwingMode === this.accessory.context.SwingMode) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set SwingMode: ${value}`); + } else { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set SwingMode: ${value}`); + } + + this.Fan.SwingMode = value; + this.doPlugUpdate.next(); + } + + async updateHomeKitCharacteristics(): Promise { + // Active + if (this.Fan.Active === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`); + } else { + this.accessory.context.Active = this.Fan.Active; + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`); + } + // RotationSpeed + if(this.Fan.RotationSpeed === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`); + } else { + this.accessory.context.RotationSpeed = this.Fan.RotationSpeed; + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`); + } + // SwingMode + if(this.Fan.SwingMode === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`); + } else { + this.accessory.context.SwingMode = this.Fan.SwingMode; + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`); + } + // BateryLevel + if(this.Battery.BatteryLevel === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); + } else { + this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); + } + // ChargingState + if(this.Battery.ChargingState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`); + } else { + this.accessory.context.ChargingState = this.Battery.ChargingState; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ChargingState: ${this.Battery.ChargingState}`); + } + // StatusLowBattery + if(this.Battery.StatusLowBattery === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); + } else { + this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.Battery.StatusLowBattery}`); + } + } + + async BLEPushConnection() { + if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`); + await this.openAPIpushChanges(); + } + } + + async BLERefreshConnection(switchbot: any): Promise { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:` + + ` ${JSON.stringify(switchbot)}`); + if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`); + await this.openAPIRefreshStatus(); + } + } + + async offlineOff(): Promise { + if (this.device.offline) { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, 0); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.hap.Characteristic.SwingMode.SWING_DISABLED); + } + } + + async apiError(e: any): Promise { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, e); + } +} diff --git a/src/device/hub.ts b/src/device/hub.ts index 552c666c..0c5c7c81 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -7,6 +7,7 @@ import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; import { Devices, device, deviceStatus, devicesConfig } from '../settings.js'; import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; +import { convertUnits } from '../utils.js'; export class Hub extends deviceBase { // Services @@ -145,34 +146,36 @@ export class Hub extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity, lightLevel } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity, lightLevel) = ' + - `Webhook:(${temperature}, ${humidity}, ${lightLevel}), ` + + const { temperature, humidity, lightLevel } = context; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.hub?.convertUnitTo === undefined) { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` + + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`); + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(scale, temperature, humidity, lightLevel) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); - if (!this.device.hub?.hide_humidity) { + if (!this.device.hub?.hide_humidity) { this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (!this.device.hub?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = temperature; - } - if (!this.device.hub?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); - const spaceBetweenLevels = 19; - await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); - } - this.updateHomeKitCharacteristics(); } + if (!this.device.hub?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.hub?.convertUnitTo); + } + if (!this.device.hub?.hide_lightsensor) { + const set_minLux = await this.minLux(); + const set_maxLux = await this.maxLux(); + const spaceBetweenLevels = 19; + await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); + } + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); @@ -215,6 +218,11 @@ export class Hub extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } async refreshStatus(): Promise { diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 29d682ae..4dbb3799 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -143,8 +143,8 @@ export class Humidifier extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -270,6 +270,11 @@ export class Humidifier extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } async refreshStatus(): Promise { @@ -670,7 +675,7 @@ export class Humidifier extends deviceBase { } else { this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 5116569a..0d5dd227 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -7,6 +7,7 @@ import { SwitchBotPlatform } from '../platform.js'; import { Subject, interval, skipWhile } from 'rxjs'; import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +import { convertUnits } from '../utils.js'; /** * Platform Accessory @@ -142,27 +143,29 @@ export class IOSensor extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - if (this.device.iosensor?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (this.device.iosensor?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = temperature; - } - this.updateHomeKitCharacteristics(); + const { temperature, humidity } = context; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.iosensor?.convertUnitTo === undefined) { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` + + `${context.scale}, Use the *convertUnitsTo* config under Indoor/Outdoor Sensor settings, if displaying incorrectly in HomeKit.`); } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(scale, temperature, humidity) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + if (this.device.iosensor?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (this.device.iosensor?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); + } + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); @@ -226,6 +229,11 @@ export class IOSensor extends deviceBase { } // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -403,10 +411,10 @@ export class IOSensor extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { if (!this.device.iosensor?.hide_humidity) { - this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); } if (!this.device.iosensor?.hide_temperature) { - this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 251a969d..35e13977 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -149,8 +149,8 @@ export class StripLight extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -160,8 +160,22 @@ export class StripLight extends deviceBase { '(powerState, brightness, color, colorTemperature) = ' + `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); + + // On this.LightBulb.On = powerState === 'ON' ? true : false; + if (this.accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness this.LightBulb.Brightness = brightness; + if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); const [red, green, blue] = color!.split(':'); @@ -176,12 +190,27 @@ export class StripLight extends deviceBase { // Hue this.LightBulb.Hue = hue; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); + if (this.accessory.context.Hue !== this.LightBulb.Hue) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } // Saturation this.LightBulb.Saturation = saturation; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); - + if (this.accessory.context.Saturation !== this.LightBulb.Saturation) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } + + // ColorTemperature + this.LightBulb.ColorTemperature = colorTemperature; + if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -298,6 +327,11 @@ export class StripLight extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -470,7 +504,7 @@ export class StripLight extends deviceBase { this.accessory.context.kelvin = kelvin; } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` - + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` diff --git a/src/device/lock.ts b/src/device/lock.ts index 162e7619..9a9781fc 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -141,8 +141,8 @@ export class Lock extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -257,6 +257,11 @@ export class Lock extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -526,7 +531,7 @@ export class Lock extends deviceBase { this.accessory.context.ContactSensorState = this.ContactSensor!.ContactSensorState; this.ContactSensor!.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensor!.ContactSensorState); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` ContactSensorState: ${this.ContactSensor!.ContactSensorState}`); + + ` ContactSensorState: ${this.ContactSensor!.ContactSensorState}`); } } if (this.LockMechanism.LockTargetState === undefined) { @@ -535,7 +540,7 @@ export class Lock extends deviceBase { this.accessory.context.LockTargetState = this.LockMechanism.LockTargetState; this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.LockMechanism.LockTargetState); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: ${this.LockMechanism.LockTargetState}`); + + ` LockTargetState: ${this.LockMechanism.LockTargetState}`); } if (this.LockMechanism.LockCurrentState === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockCurrentState: ${this.LockMechanism.LockCurrentState}`); @@ -543,7 +548,7 @@ export class Lock extends deviceBase { this.accessory.context.LockCurrentState = this.LockMechanism.LockCurrentState; this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.LockMechanism.LockCurrentState); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockCurrentState: ${this.LockMechanism.LockCurrentState}`); + + ` LockCurrentState: ${this.LockMechanism.LockCurrentState}`); } if (this.Battery.BatteryLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); @@ -551,7 +556,7 @@ export class Lock extends deviceBase { this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` BatteryLevel: ${this.Battery.BatteryLevel}`); + + ` BatteryLevel: ${this.Battery.BatteryLevel}`); } if (this.Battery.StatusLowBattery === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); @@ -559,7 +564,7 @@ export class Lock extends deviceBase { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 18cac154..db4cdd85 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -4,6 +4,7 @@ */ import { deviceBase } from './device.js'; import { SwitchBotPlatform } from '../platform.js'; +import { convertUnits } from '../utils.js'; import { Subject, interval, skipWhile } from 'rxjs'; import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; @@ -137,27 +138,29 @@ export class Meter extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - if (this.device.meter?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (this.device.meter?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = temperature; - } - this.updateHomeKitCharacteristics(); + const { temperature, humidity } = context; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.meter?.convertUnitTo === undefined) { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook scale: ` + + `${context.scale}, instead of CELCIUS. Use the *convertUnitsTo* config under Meter settings, if displaying incorrectly in HomeKit.`); } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(scale, temperature, humidity) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.meter?.convertUnitTo)}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + if (this.device.meter?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (this.device.meter?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.meter?.convertUnitTo); + } + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); @@ -222,6 +225,11 @@ export class Meter extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -318,7 +326,7 @@ export class Meter extends deviceBase { if (!this.device.meter?.hide_humidity) { if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, @@ -369,7 +377,7 @@ export class Meter extends deviceBase { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); if (this.device.mqttURL) { mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } @@ -401,10 +409,10 @@ export class Meter extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { if (!this.device.meter?.hide_humidity) { - this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); } if (!this.device.meter?.hide_temperature) { - this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 47b815a4..d5a61e1a 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -142,8 +142,8 @@ export class MeterPlus extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -226,6 +226,11 @@ export class MeterPlus extends deviceBase { } // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -322,7 +327,7 @@ export class MeterPlus extends deviceBase { if (!this.device.meter?.hide_humidity) { if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, @@ -374,7 +379,7 @@ export class MeterPlus extends deviceBase { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); if (this.device.mqttURL) { mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`); } @@ -406,10 +411,10 @@ export class MeterPlus extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { if (!this.device.meter?.hide_humidity) { - this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); + this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50); } if (!this.device.meter?.hide_temperature) { - this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); + this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30); } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100); } diff --git a/src/device/motion.ts b/src/device/motion.ts index 0d3d551b..5c167695 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -49,7 +49,7 @@ export class Motion extends deviceBase { // Initialize Motion Sensor property this.MotionSensor = { Service: accessory.getService(this.hap.Service.MotionSensor)!, - MotionDetected:accessory.context.MotionDetected || false, + MotionDetected: accessory.context.MotionDetected || false, }; // Retrieve initial values and updateHomekit @@ -100,8 +100,8 @@ export class Motion extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -201,6 +201,11 @@ export class Motion extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** diff --git a/src/device/plug.ts b/src/device/plug.ts index 02e51dd8..bb9f7abf 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -62,8 +62,8 @@ export class Plug extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -131,6 +131,11 @@ export class Plug extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -340,7 +345,7 @@ export class Plug extends deviceBase { } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` - + `On: ${this.Outlet.On}, OnCached: ${this.accessory.context.On}`); + + `On: ${this.Outlet.On}, OnCached: ${this.accessory.context.On}`); } } diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 0fabfc77..6990af96 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -101,8 +101,8 @@ export class RobotVacuumCleaner extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); @@ -200,6 +200,11 @@ export class RobotVacuumCleaner extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } /** @@ -369,7 +374,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); if (this.LightBulb.On !== this.accessory.context.On) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges` - + ` On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`); + + ` On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -398,7 +403,7 @@ export class RobotVacuumCleaner extends deviceBase { .then(() => { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); + + `On: ${this.LightBulb.On} sent over BLE, sent successfully`); this.LightBulb.On = false; }) .catch(async (e: any) => { @@ -411,7 +416,7 @@ export class RobotVacuumCleaner extends deviceBase { }); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,` - + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); + + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); } } @@ -435,7 +440,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${bodyChange} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -474,7 +479,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -595,7 +600,7 @@ export class RobotVacuumCleaner extends deviceBase { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 4e4fa747..88d4f7f2 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -101,18 +101,20 @@ export class WaterDetector extends deviceBase { }); //regisiter webhook event handler - if (this.device.webhook && !this.device.waterdetector?.hide_leak) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + if (device.webhook && !this.device.waterdetector?.hide_leak) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { status } = context; + const { detectionState, battery } = context; const { LeakDetected } = this.LeakSensor!; + const { BatteryLevel } = this.Battery!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(status) = ' + - `Webhook:(${status}), ` + - `current:(${LeakDetected})`); - this.LeakSensor!.LeakDetected = status; + '(detectionState, battery) = ' + + `Webhook:(${detectionState}, ${battery}), ` + + `current:(${LeakDetected}, ${BatteryLevel})`); + this.LeakSensor!.LeakDetected = detectionState; + this.Battery!.BatteryLevel = battery; this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -165,6 +167,11 @@ export class WaterDetector extends deviceBase { // FirmwareRevision this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); } async refreshStatus(): Promise { diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index 830f8908..6ac665e5 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -348,7 +348,8 @@ export class AirConditioner extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -616,7 +617,7 @@ export class AirConditioner extends irdeviceBase { this.accessory.context.RotationSpeed = this.HeaterCooler.RotationSpeed; this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.HeaterCooler.RotationSpeed); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); + + ` RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); } // CurrentTemperature if (this.HeaterCooler.CurrentTemperature === undefined) { @@ -654,7 +655,7 @@ export class AirConditioner extends irdeviceBase { // CurrentHeaterCoolerState if (this.HeaterCooler.CurrentHeaterCoolerState === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); + + ` CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); } else { this.accessory.context.CurrentHeaterCoolerState = this.HeaterCooler.CurrentHeaterCoolerState; this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.HeaterCooler.CurrentHeaterCoolerState); diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index cfa760d5..261278f7 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -224,7 +224,8 @@ export class AirPurifier extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -261,7 +262,7 @@ export class AirPurifier extends irdeviceBase { this.accessory.context.CurrentAirPurifierState = this.AirPurifier.CurrentAirPurifierState; this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, this.AirPurifier.CurrentAirPurifierState); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentAirPurifierState: ${this.AirPurifier.CurrentAirPurifierState}`); + + ` CurrentAirPurifierState: ${this.AirPurifier.CurrentAirPurifierState}`); } // CurrentHeaterCoolerState if (this.CurrentHeaterCoolerState === undefined) { @@ -281,7 +282,7 @@ export class AirPurifier extends irdeviceBase { this.accessory.context.CurrentTemperature = this.TemperatureSensor.CurrentTemperature; this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`); + + ` CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`); } } diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 3d22dad1..6b3ecd6b 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -68,7 +68,7 @@ export class Camera extends irdeviceBase { */ async pushOnChanges(): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${this.Switch.On},` - + ` disablePushOn: ${this.disablePushOn}`); + + ` disablePushOn: ${this.disablePushOn}`); if (this.Switch.On && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); @@ -114,7 +114,8 @@ export class Camera extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 3b518eeb..c2f1508a 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -13,7 +13,7 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; * An instance of this class is created for each accessory your platform registers * Each accessory may expose multiple services of different service types. */ -export class Fan extends irdeviceBase { +export class IRFan extends irdeviceBase { // Services private Fan: { Service: Service; @@ -233,7 +233,8 @@ export class Fan extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index b494c5c8..03787970 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -455,7 +455,7 @@ export abstract class irdeviceBase { break; default: this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: ` - + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); } } diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 3c58e7af..2ad2194d 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -225,7 +225,8 @@ export class Light extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.accessory.context.On = this.On; this.updateHomeKitCharacteristics(); } else { diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 639bcad7..7bdb391f 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -440,7 +440,7 @@ export class Others extends irdeviceBase { */ async pushOnChanges(On: boolean): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOnChanges On: ${On},` - +` disablePushOn: ${this.disablePushOn}, customize: ${this.device.customize}, customOn: ${this.device.customOn}`); + + ` disablePushOn: ${this.disablePushOn}, customize: ${this.device.customize}, customOn: ${this.device.customOn}`); if (this.device.customize) { if (On === true && !this.disablePushOn) { const commandType: string = await this.commandType(); @@ -493,7 +493,8 @@ export class Others extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -502,11 +503,11 @@ export class Others extends irdeviceBase { } catch (e: any) { this.apiError(e); this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` - + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } @@ -519,12 +520,12 @@ export class Others extends irdeviceBase { this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN); this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); } else { this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetDoorState: Closed, CurrentDoorState: Closed (${this.GarageDoor!.On})`); + + ` TargetDoorState: Closed, CurrentDoorState: Closed (${this.GarageDoor!.On})`); } } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor!.On}`); @@ -537,13 +538,13 @@ export class Others extends irdeviceBase { this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door!.On})`); } else { this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door!.On})`); } } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Door On: ${this.Door!.On}`); @@ -556,13 +557,13 @@ export class Others extends irdeviceBase { this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window!.On})`); } else { this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window!.On})`); } } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window On: ${this.Window!.On}`); @@ -575,13 +576,13 @@ export class Others extends irdeviceBase { this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering!.On})`); } else { this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering!.On})`); } } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); @@ -593,7 +594,7 @@ export class Others extends irdeviceBase { this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURE (${this.Lock!.On})`); + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURE (${this.Lock!.On})`); } else { this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); @@ -637,13 +638,13 @@ export class Others extends irdeviceBase { this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch!.On})`); + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch!.On})`); } else { this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch!.On})`); + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch!.On})`); } } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch!.On}`); @@ -701,90 +702,90 @@ export class Others extends irdeviceBase { async removeOutletService(accessory: PlatformAccessory): Promise { // If Outlet.Service still present, then remove first if (this.Outlet?.Service) { - this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); - accessory.removeService(this.Outlet!.Service); + this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); + accessory.removeService(this.Outlet!.Service); } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If GarageDoor.Service still present, then remove first if (this.GarageDoor?.Service) { - this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - accessory.removeService(this.GarageDoor!.Service); + this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); + accessory.removeService(this.GarageDoor!.Service); } } async removeDoorService(accessory: PlatformAccessory): Promise { // If Door.Service still present, then remove first if (this.Door?.Service) { - this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); - accessory.removeService(this.Door!.Service); + this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); + accessory.removeService(this.Door!.Service); } } async removeLockService(accessory: PlatformAccessory): Promise { // If Lock.Service still present, then remove first if (this.Lock?.Service) { - this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.Lock!.Service); + this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); + accessory.removeService(this.Lock!.Service); } } async removeFaucetService(accessory: PlatformAccessory): Promise { // If Faucet.Service still present, then remove first if (this.Faucet?.Service) { - this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); - accessory.removeService(this.Faucet!.Service); + this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); + accessory.removeService(this.Faucet!.Service); } } async removeFanService(accessory: PlatformAccessory): Promise { // If Fan Service still present, then remove first if (this.Fan?.Service) { - this.Fan!.Service = this.accessory.getService(this.hap.Service.Fan) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); - accessory.removeService(this.Fan!.Service); + this.Fan!.Service = this.accessory.getService(this.hap.Service.Fan) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); + accessory.removeService(this.Fan!.Service); } } async removeWindowService(accessory: PlatformAccessory): Promise { // If Window.Service still present, then remove first if (this.Window?.Service) { - this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); - accessory.removeService(this.Window!.Service); + this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); + accessory.removeService(this.Window!.Service); } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If WindowCovering.Service still present, then remove first if (this.WindowCovering?.Service) { - this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - accessory.removeService(this.WindowCovering!.Service); + this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); + accessory.removeService(this.WindowCovering!.Service); } } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If StatefulProgrammableSwitch.Service still present, then remove first if (this.StatefulProgrammableSwitch?.Service) { - this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); - accessory.removeService(this.StatefulProgrammableSwitch!.Service); + this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); + accessory.removeService(this.StatefulProgrammableSwitch!.Service); } } async removeSwitchService(accessory: PlatformAccessory): Promise { // If Switch.Service still present, then remove first if (this.Switch?.Service) { - this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; - this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); - accessory.removeService(this.Switch!.Service); + this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); + accessory.removeService(this.Switch!.Service); } } diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 0d5ef88f..cf76edb4 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -390,7 +390,8 @@ export class TV extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 923c491d..7b98ccc8 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -109,7 +109,8 @@ export class VacuumCleaner extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index fc1e54a9..ef73d444 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -114,7 +114,8 @@ export class WaterHeater extends irdeviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.successLog(`${this.device.remoteType}: ${this.accessory.displayName} request to SwitchBot API, body: ${bodyChange} sent successfully`); + this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/platform.ts b/src/platform.ts index 0051835c..73db8443 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -20,8 +20,9 @@ import { CeilingLight } from './device/ceilinglight.js'; import { StripLight } from './device/lightstrip.js'; import { Humidifier } from './device/humidifier.js'; import { RobotVacuumCleaner } from './device/robotvacuumcleaner.js'; +import { Fan } from './device/fan.js'; import { TV } from './irdevice/tv.js'; -import { Fan } from './irdevice/fan.js'; +import { IRFan } from './irdevice/fan.js'; import { Light } from './irdevice/light.js'; import { Others } from './irdevice/other.js'; import { Camera } from './irdevice/camera.js'; @@ -727,6 +728,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { case 'WoSweeperMini': case 'Robot Vacuum Cleaner S1': case 'Robot Vacuum Cleaner S1 Plus': + case 'Robot Vacuum Cleaner S10': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createRobotVacuumCleaner(device); break; @@ -747,6 +749,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { case 'remote with screen+': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId} is Not Supported.`); break; + case 'Battery Circulator Fan': + this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); + this.createFan(device); + break; default: this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`); // eslint-disable-next-line max-len @@ -782,7 +788,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { case 'Fan': case 'DIY Fan': this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`); - this.createFan(device); + this.createIRFan(device); break; case 'Air Conditioner': case 'DIY Air Conditioner': @@ -1814,6 +1820,66 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } + private async createFan(device: device & devicesConfig) { + const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`); + + // see if an accessory with the same uuid has already been registered and restored from + // the cached devices we stored in the `configureAccessory` method above + const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid); + + if (existingAccessory) { + // the accessory already exists + if (await this.registerDevice(device)) { + // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: + existingAccessory.context.model = device.deviceType; + existingAccessory.context.deviceID = device.deviceId; + existingAccessory.displayName = device.configDeviceName || device.deviceName; + if (device.firmware) { + existingAccessory.context.FirmwareRevision = device.firmware; + } + existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; + this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); + existingAccessory.context.connectionType = await this.connectionType(device); + this.api.updatePlatformAccessories([existingAccessory]); + // create the accessory handler for the restored accessory + // this is imported from `platformAccessory.ts` + new Fan(this, existingAccessory, device); + this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`); + } else { + this.unregisterPlatformAccessories(existingAccessory); + } + } else if (await this.registerDevice(device)) { + // the accessory does not yet exist, so we need to create it + if (!device.external) { + this.infoLog(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); + } + + // create a new accessory + const accessory = new this.api.platformAccessory(device.deviceName, uuid); + + // store a copy of the device object in the `accessory.context` + // the `context` property can be used to store any data about the accessory you may need + accessory.context.device = device; + accessory.context.model = device.deviceType; + accessory.context.deviceID = device.deviceId; + if (device.firmware) { + accessory.context.FirmwareRevision = device.firmware; + } + accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; + accessory.context.connectionType = await this.connectionType(device); + // create the accessory handler for the newly create accessory + // this is imported from `platformAccessory.ts` + new Fan(this, accessory, device); + this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`); + + // publish device externally or link the accessory to your platform + this.externalOrPlatform(device, accessory); + this.accessories.push(accessory); + } else { + this.debugLog(`Device not registered: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); + } + } + private async createRobotVacuumCleaner(device: device & devicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`); @@ -1926,7 +1992,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createFan(device: irdevice & devicesConfig) { + private async createIRFan(device: irdevice & devicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`); // see if an accessory with the same uuid has already been registered and restored from @@ -1949,7 +2015,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.api.updatePlatformAccessories([existingAccessory]); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` - new Fan(this, existingAccessory, device); + new IRFan(this, existingAccessory, device); this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${existingAccessory.UUID})`); } else { this.unregisterPlatformAccessories(existingAccessory); @@ -1975,7 +2041,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` - new Fan(this, accessory, device); + new IRFan(this, accessory, device); this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`); // publish device externally or link the accessory to your platform diff --git a/src/settings.ts b/src/settings.ts index 75873809..572eab36 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -48,6 +48,9 @@ export type options = { }; export interface devicesConfig extends device { + bleMac?: string; + model?: string; + bleModel?: string; configDeviceType: string; configDeviceName?: string; deviceId: string; @@ -89,11 +92,13 @@ export interface devicesConfig extends device { export type meter = { hide_temperature?: boolean; + convertUnitTo?: string; hide_humidity?: boolean; }; export type iosensor = { hide_temperature?: boolean; + convertUnitTo?: string; hide_humidity?: boolean; }; @@ -174,6 +179,7 @@ export type lock = { export type hub = { hide_temperature?: boolean; + convertUnitTo?: string; hide_humidity?: boolean; hide_lightsensor?: boolean; }; @@ -271,14 +277,28 @@ export type device = { slidePosition?: string; //the version of the device version?: number; - //BLE Mac Address - bleMac?: string; - //Device Model - model?: string; - //Device BLE Model - bleModel?: string; + // Fan Mode: direct mode: direct; natural mode: "natural"; sleep mode: "sleep"; ultra quiet mode: "baby" + mode: string; + //the current battery level + battery: number; + //ON/OFF state + power: string; + //set nightlight status. turn off: off; mode 1: 1; mode 2: 2 + nightStatus: number; + //set horizontal oscillation. turn on: on; turn off: off + oscillation: string; + //set vertical oscillation. turn on: on; turn off: off + verticalOscillation: string; + //battery charge status. charging or uncharged + chargingStatus: string; + //fan speed. 1~100 + fanSpeed: number; }; +// values defined but not displayed by API +export interface deviceInfo extends device { +} + //a list of virtual infrared remote devices. export type infraredRemoteList = { device: Array; @@ -300,7 +320,7 @@ export type deviceStatus = { export type deviceStatusBody = { //v1.1 of API - deviceId: string; //device ID. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) + deviceId: string; //device ID. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt, Battery Circulator Fan) deviceType: string; //device type. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) hubDeviceId: string; //device's parent Hub ID. 000000000000 when the device itself is a Hub or it is connected through Wi-Fi. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) power?: string; //ON/OFF state. (Used by the following deviceTypes: Bot, Ceiling Light, Ceiling Light Pro, PLug, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Humidifier) @@ -330,16 +350,21 @@ export type deviceStatusBody = { childLock?: boolean; //determines if a Humidifier's safety lock is on or not. (Used by the following deviceTypes: Humidifier) sound?: boolean; //determines if a Humidifier is muted or not. (Used by the following deviceTypes: Humidifier) lackWater?: boolean; //determines if the water tank is empty or not. (Used by the following deviceTypes: Humidifier) - version?: number; //the version of the device. (Used by the following deviceTypes: Blind Tilt, Meter, MeterPlus, IOSensor) + version?: number; //the version of the device. direction?: string; //the opening direction of a Blind Tilt device. (Used by the following deviceTypes: Blind Tilt) runStatus?: string; //'static' when not moving. (Used by the following deviceTypes: Blind Tilt) - mode?: number; //available for devices. the fan mode. (Used by the following deviceTypes: Smart Fan) + mode?: number | string; //available for devices. the fan mode. (Used by the following deviceTypes: Smart Fan, Battery Circulator Fan):(direct mode: direct; natural mode: "natural"; sleep mode: "sleep"; ultra quiet mode: "baby") speed?: number; //the fan speed. (Used by the following deviceTypes: Smart Fan) shaking?: boolean; //determines if the fan is swinging or not. (Used by the following deviceTypes: Smart Fan) shakeCenter?: string; //the fan's swing direction. (Used by the following deviceTypes: Smart Fan) shakeRange?: string; //the fan's swing range, 0~120°. (Used by the following deviceTypes: Smart Fan) status?: number //the leak status. 0 for no leak, 1 for leak. (Used by the following deviceTypes: Water Detector) lightLevel?: number; //the light level. (Used by the following deviceTypes: Hub) + nightStatus: number // set nightlight status. turn off: off; mode 1: 1; mode 2: 2 + oscillation: string // set horizontal oscillation. turn on: on; turn off: off + verticalOscillation: string // set vertical oscillation. turn on: on; turn off: off + chargingStatus: string // battery charge status. charging or uncharged + fanSpeed: number // fan speed. 1~100 }; export type ad = { diff --git a/src/utils.ts b/src/utils.ts index 5f570481..265cc934 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -19,8 +19,8 @@ export enum SwitchBotModel { ContactSensor = 'W1201500', ColorBulb = 'W1401400', StripLight = 'W1701100', - PlugMiniUS = 'W1901400', - PlugMiniJP = 'W2001400', + PlugMiniUS = 'W1901400/W1901401', + PlugMiniJP = 'W2001400/W2001401', Lock = 'W1601700', LockPro = 'W3500000', Keypad = 'W2500010', @@ -30,16 +30,17 @@ export enum SwitchBotModel { WoSweeperMini = 'WoSweeperMini', RobotVacuumCleanerS1 = 'W3011000', // Currently only available in Japan. RobotVacuumCleanerS1Plus = 'W3011010', // Currently only available in Japan. + RobotVacuumCleanerS10 = 'W3211800', Remote = 'Remote', UniversalRemote = 'UniversalRemote', - CeilingLight = 'W2612230', // Currently only available in Japan. - CeilingLightPro = 'W2612210', // Currently only available in Japan. + CeilingLight = 'W2612230/W2612240', // Currently only available in Japan. + CeilingLightPro = 'W2612210/W2612220', // Currently only available in Japan. IndoorCam = 'W1301200', PanTiltCam = 'W1801200', PanTiltCam2K = 'W3101100', BlindTilt = 'W2701600', BatteryCirculatorFan = 'W3800510', - WaterDetector = 'WoWaterDetector', + WaterDetector = 'W4402000', Unknown = 'Unknown', } @@ -77,11 +78,25 @@ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +/** + * Converts the value to celsius if the temperature units are in Fahrenheit +**/ +export function convertUnits(value: number, unit: string, convert?: string): number { + if (unit === 'CELSIUS' && convert === 'CELSIUS') { + return Math.round((value * 9) / 5 + 32); + } else if (unit === 'FAHRENHEIT' && convert === 'FAHRENHEIT') { + // celsius should be to the nearest 0.5 degree + return Math.round((5 / 9) * (value - 32) * 2) / 2; + } + return value; +} + + export function rgb2hs(r: any, g: any, b: any) { - /* - Credit: - https://github.com/WickyNilliams/pure-color - */ +/** + * Credit: + * https://github.com/WickyNilliams/pure-color +**/ r = parseInt(r); g = parseInt(g); b = parseInt(b); From 93d7903cba62eff40efb2b701a5f9df5514f6979 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:10:58 -0500 Subject: [PATCH 32/73] cleaner --- src/device/contact.ts | 4 ++-- src/device/hub.ts | 6 +++--- src/device/humidifier.ts | 2 +- src/device/iosensor.ts | 4 ++-- src/device/lock.ts | 2 +- src/device/meter.ts | 4 ++-- src/device/meterplus.ts | 4 ++-- src/device/motion.ts | 2 +- src/device/waterdetector.ts | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/device/contact.ts b/src/device/contact.ts index d9af719a..908efec4 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -89,7 +89,7 @@ export class Contact extends deviceBase { this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; accessory.removeService(this.MotionSensor!.Service); - } else if (!this.MotionSensor!.Service && !device.contact?.hide_motionsensor) { + } else if (!this.MotionSensor?.Service && !device.contact?.hide_motionsensor) { this.debugLog(`${device.deviceType}: ${accessory.displayName} Add Motion Sensor Service`); const MotionService = `${accessory.displayName} Motion Sensor`; (this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) @@ -105,7 +105,7 @@ export class Contact extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor!.Service && !device.contact?.hide_lightsensor) { + } else if (!this.LightSensor?.Service && !device.contact?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); const LightSensorService = `${accessory.displayName} Light Sensor`; diff --git a/src/device/hub.ts b/src/device/hub.ts index 0c5c7c81..291544e6 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -72,7 +72,7 @@ export class Hub extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor!.Service) { + } else if (!this.TemperatureSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) @@ -100,7 +100,7 @@ export class Hub extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor!.Service) { + } else if (!this.HumiditySensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) @@ -124,7 +124,7 @@ export class Hub extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor!.Service) { + } else if (!this.LightSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); const LightSensorService = `${accessory.displayName} Light Sensor`; (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 4dbb3799..86c55751 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -110,7 +110,7 @@ export class Humidifier extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor!.Service && !this.BLE) { + } else if (!this.TemperatureSensor?.Service && !this.BLE) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 0d5dd227..2fecfeec 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -77,7 +77,7 @@ export class IOSensor extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor!.Service) { + } else if (!this.TemperatureSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) @@ -105,7 +105,7 @@ export class IOSensor extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor!.Service) { + } else if (!this.HumiditySensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) diff --git a/src/device/lock.ts b/src/device/lock.ts index 9a9781fc..9ec56bb4 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -111,7 +111,7 @@ export class Lock extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; accessory.removeService(this.ContactSensor!.Service); - } else if (!this.ContactSensor!.Service) { + } else if (!this.ContactSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Contact Sensor Service`); const ContactSensorService = `${accessory.displayName} Contact Sensor`; (this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) diff --git a/src/device/meter.ts b/src/device/meter.ts index db4cdd85..2ca0ddfc 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -72,7 +72,7 @@ export class Meter extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor!.Service) { + } else if (!this.TemperatureSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) @@ -100,7 +100,7 @@ export class Meter extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor!.Service) { + } else if (!this.HumiditySensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index d5a61e1a..f3062f5c 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -76,7 +76,7 @@ export class MeterPlus extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor!.Service) { + } else if (!this.TemperatureSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) @@ -104,7 +104,7 @@ export class MeterPlus extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor!.Service) { + } else if (!this.HumiditySensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) diff --git a/src/device/motion.ts b/src/device/motion.ts index 5c167695..a0196ee7 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -70,7 +70,7 @@ export class Motion extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor!.Service) { + } else if (!this.LightSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); const LightSensorService = `${accessory.displayName} Light Sensor`; (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 88d4f7f2..28413570 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -71,7 +71,7 @@ export class WaterDetector extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; accessory.removeService(this.LeakSensor!.Service); - } else if (!this.LeakSensor!.Service) { + } else if (!this.LeakSensor?.Service) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Leak Sensor Service`); const LeakSensorService = `${accessory.displayName} Leak Sensor`; (this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) From 799ba6a15cb98ea43eb7ccadd13712d2f70e3eaf Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:17:15 -0500 Subject: [PATCH 33/73] override statusCode when Device is Hub --- src/device/device.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/device/device.ts b/src/device/device.ts index 9dd2050c..8b18edb6 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -537,6 +537,14 @@ export abstract class deviceBase { } async statusCode(statusCode: number): Promise { + switch (this.device.deviceType) { + case this.device.hubDeviceId: + statusCode = 161; + break; + case '000000000000': + statusCode = 161; + break; + } switch (statusCode) { case 151: this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`); From a47b52b1dddc2086feeb31c27e4846b4269c23a0 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:29:15 -0500 Subject: [PATCH 34/73] correct version info --- src/device/device.ts | 9 +- src/irdevice/irdevice.ts | 6 +- src/platform.ts | 214 ++++++++++----------------------------- src/settings.ts | 2 +- 4 files changed, 65 insertions(+), 166 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 8b18edb6..6973b72f 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -528,11 +528,14 @@ export abstract class deviceBase { accessory.context.model = device.model; accessory.context.deviceId = device.deviceId; accessory.context.deviceType = device.deviceType; - if (device.firmware === undefined) { - device.firmware = this.platform.version; + + if (device.firmware) { accessory.context.FirmwareRevision = device.firmware; + } else if (device.firmware === undefined && device.version === undefined) { + device.version = this.platform.version; + accessory.context.FirmwareRevision = device.version; } else { - accessory.context.FirmwareRevision = device.firmware; + accessory.context.FirmwareRevision = device.version; } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 03787970..001726a4 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -325,11 +325,13 @@ export abstract class irdeviceBase { accessory.context.model = device.remoteType; accessory.context.deviceId = device.deviceId; accessory.context.remoteType = device.remoteType; - if (device.firmware === undefined) { + if (device.firmware) { + accessory.context.FirmwareRevision = device.firmware; + } else if (device.firmware === undefined) { device.firmware = this.platform.version; accessory.context.FirmwareRevision = device.firmware; } else { - accessory.context.FirmwareRevision = device.firmware; + accessory.context.FirmwareRevision = 'Unknown'; } } diff --git a/src/platform.ts b/src/platform.ts index 73db8443..6167d6de 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -845,9 +845,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -873,9 +871,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -905,9 +901,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -933,9 +927,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // accessory.context.FirmwareRevision = findaccessories.accessoryAttribute.softwareRevision; @@ -966,9 +958,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -994,9 +984,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1027,9 +1015,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1055,9 +1041,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1088,9 +1072,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1116,9 +1098,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1148,9 +1128,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1176,9 +1154,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1208,9 +1184,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1236,9 +1210,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1268,9 +1240,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1296,9 +1266,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1328,9 +1296,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1356,9 +1322,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1388,9 +1352,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1429,9 +1391,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1461,9 +1421,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1502,9 +1460,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1534,9 +1490,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1562,9 +1516,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1594,9 +1546,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1622,9 +1572,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1654,9 +1602,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1682,9 +1628,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1714,9 +1658,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1742,9 +1684,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1774,9 +1714,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1802,9 +1740,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1834,9 +1770,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1862,9 +1796,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1894,9 +1826,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1922,9 +1852,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1952,7 +1880,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.firmware; + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -1975,9 +1903,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2006,9 +1932,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2034,9 +1958,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2066,9 +1988,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2094,9 +2014,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2126,9 +2044,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2154,9 +2070,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2186,9 +2100,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2214,9 +2126,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2246,9 +2156,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2274,9 +2182,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2306,9 +2212,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2334,9 +2238,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2366,9 +2268,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2394,9 +2294,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2426,9 +2324,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.firmware) { - existingAccessory.context.FirmwareRevision = device.firmware; - } + existingAccessory.context.FirmwareRevision = device.version; existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2454,9 +2350,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } + accessory.context.FirmwareRevision = device.version; accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory diff --git a/src/settings.ts b/src/settings.ts index 572eab36..43b502cd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -276,7 +276,7 @@ export type device = { //the current position, 0-100 slidePosition?: string; //the version of the device - version?: number; + version?: string; // Fan Mode: direct mode: direct; natural mode: "natural"; sleep mode: "sleep"; ultra quiet mode: "baby" mode: string; //the current battery level From ca6478df947945f1673e7e901cec63568d88cd28 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:33:33 -0500 Subject: [PATCH 35/73] fixes 971 TypeError: Cannot set properties of undefined (setting 'Service') - https://github.com/OpenWonderLabs/homebridge-switchbot/issues/971#issuecomment-2116667352 --- src/device/bot.ts | 56 +++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/device/bot.ts b/src/device/bot.ts index 82986620..a7f9a052 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -1064,92 +1064,92 @@ export class Bot extends deviceBase { async removeOutletService(accessory: PlatformAccessory): Promise { // If outletService still present, then remove first - this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; if (this.Outlet?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); - } + this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); accessory.removeService(this.Outlet!.Service); + } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If garageDoorService still present, then remove first - this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; if (this.GarageDoor?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - } + this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); accessory.removeService(this.GarageDoor!.Service); + } } async removeDoorService(accessory: PlatformAccessory): Promise { // If doorService still present, then remove first - this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; if (this.Door?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); - } + this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); accessory.removeService(this.Door!.Service); + } } async removeLockService(accessory: PlatformAccessory): Promise { // If lockService still present, then remove first - this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; if (this.Lock?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); - } + this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); accessory.removeService(this.Lock!.Service); + } } async removeFaucetService(accessory: PlatformAccessory): Promise { // If faucetService still present, then remove first - this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; if (this.Faucet?.Service) { + this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Faucet Service`); + accessory.removeService(this.Faucet!.Service); } - accessory.removeService(this.Faucet!.Service); } async removeFanService(accessory: PlatformAccessory): Promise { // If fanService still present, then remove first - this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; if (this.Fan?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); - } + this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); accessory.removeService(this.Fan!.Service); + } } async removeWindowService(accessory: PlatformAccessory): Promise { // If windowService still present, then remove first - this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; if (this.Window?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); - } + this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); accessory.removeService(this.Window!.Service); + } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If windowCoveringService still present, then remove first - this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; if (this.WindowCovering?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - } + this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); accessory.removeService(this.WindowCovering!.Service); + } } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If statefulProgrammableSwitchService still present, then remove first - this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; if (this.StatefulProgrammableSwitch?.Service) { + this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); + accessory.removeService(this.StatefulProgrammableSwitch!.Service); } - accessory.removeService(this.StatefulProgrammableSwitch!.Service); } async removeSwitchService(accessory: PlatformAccessory): Promise { // If switchService still present, then remove first - this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; if (this.Switch?.Service) { - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); - } + this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); accessory.removeService(this.Switch!.Service); + } } async getOn(): Promise { From 846ca97e82f4375348826bbae79e23a02b713484 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:36:43 -0500 Subject: [PATCH 36/73] context.FirmwareRevision === undefined --- src/device/device.ts | 2 +- src/irdevice/irdevice.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 6973b72f..d12725e1 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -531,7 +531,7 @@ export abstract class deviceBase { if (device.firmware) { accessory.context.FirmwareRevision = device.firmware; - } else if (device.firmware === undefined && device.version === undefined) { + } else if ((device.firmware === undefined && device.version === undefined) || accessory.context.FirmwareRevision === undefined) { device.version = this.platform.version; accessory.context.FirmwareRevision = device.version; } else { diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 001726a4..a9917200 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -327,7 +327,7 @@ export abstract class irdeviceBase { accessory.context.remoteType = device.remoteType; if (device.firmware) { accessory.context.FirmwareRevision = device.firmware; - } else if (device.firmware === undefined) { + } else if (device.firmware === undefined || accessory.context.FirmwareRevision === undefined) { device.firmware = this.platform.version; accessory.context.FirmwareRevision = device.firmware; } else { From 159e65be950a27c47669d3fe61783fc35b1e75a0 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 00:54:43 -0500 Subject: [PATCH 37/73] more version fixes --- src/device/device.ts | 4 +- src/platform.ts | 216 ++++++++++++++++++++++++++++++++----------- 2 files changed, 164 insertions(+), 56 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index d12725e1..4b1694ea 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -531,9 +531,9 @@ export abstract class deviceBase { if (device.firmware) { accessory.context.FirmwareRevision = device.firmware; - } else if ((device.firmware === undefined && device.version === undefined) || accessory.context.FirmwareRevision === undefined) { + } else if (device.firmware === undefined && device.version === undefined && accessory.context.FirmwareRevision === undefined) { device.version = this.platform.version; - accessory.context.FirmwareRevision = device.version; + accessory.context.FirmwareRevision = this.platform.version; } else { accessory.context.FirmwareRevision = device.version; } diff --git a/src/platform.ts b/src/platform.ts index 6167d6de..89e4a5da 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -845,7 +845,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -871,7 +873,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -901,7 +905,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -927,7 +933,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // accessory.context.FirmwareRevision = findaccessories.accessoryAttribute.softwareRevision; @@ -958,7 +966,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -984,7 +994,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1015,7 +1027,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1041,7 +1055,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1072,7 +1088,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1098,7 +1116,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1128,7 +1148,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1154,7 +1176,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1184,7 +1208,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1210,7 +1236,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1240,7 +1268,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1266,7 +1296,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1296,7 +1328,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1322,7 +1356,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1352,7 +1388,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1391,7 +1429,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1421,7 +1461,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1460,7 +1502,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1490,7 +1534,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1516,7 +1562,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1546,7 +1594,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1572,7 +1622,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1602,7 +1654,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1628,7 +1682,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1658,7 +1714,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1684,7 +1742,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1714,7 +1774,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1740,7 +1802,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1770,7 +1834,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1796,7 +1862,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1826,7 +1894,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = await this.connectionType(device); @@ -1852,7 +1922,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); // create the accessory handler for the newly create accessory @@ -1880,7 +1952,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -1903,7 +1977,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -1932,7 +2008,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -1958,7 +2036,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -1988,7 +2068,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2014,7 +2096,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2044,7 +2128,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2070,7 +2156,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2100,7 +2188,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2126,7 +2216,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2156,7 +2248,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2182,7 +2276,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2212,7 +2308,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2238,7 +2336,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2268,7 +2368,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2294,7 +2396,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory @@ -2324,7 +2428,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - existingAccessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + existingAccessory.context.FirmwareRevision = device.version; + } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); existingAccessory.context.connectionType = device.connectionType; @@ -2350,7 +2456,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - accessory.context.FirmwareRevision = device.version; + if (device.version && !device.firmware) { + accessory.context.FirmwareRevision = device.version; + } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; // create the accessory handler for the newly create accessory From 394e1f2e7e96549440ec319b01e7f52beb247137 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 01:19:42 -0500 Subject: [PATCH 38/73] Update device.ts --- src/device/device.ts | 103 +++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 4b1694ea..9841e2ac 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -179,91 +179,108 @@ export abstract class deviceBase { } async getDeviceConfigSettings(device: device & devicesConfig): Promise { - let config = {}; - if (device.logging !== undefined) { - config['logging'] = device.logging; + const deviceConfig = {}; + if (device.logging !== 'standard') { + deviceConfig['logging'] = device.logging; } - if (device.refreshRate !== undefined) { - config['refreshRate'] = device.refreshRate; + if (device.refreshRate !== 0) { + deviceConfig['refreshRate'] = device.refreshRate; } - if (device.updateRate !== undefined) { - config['updateRate'] = device.updateRate; + if (device.updateRate !== 0) { + deviceConfig['updateRate'] = device.updateRate; } - if (device.scanDuration !== undefined) { - config['scanDuration'] = device.scanDuration; + if (device.scanDuration !== 0) { + deviceConfig['scanDuration'] = device.scanDuration; } - if (device.offline !== undefined) { - config['offline'] = device.offline; + if (device.offline === true) { + deviceConfig['offline'] = device.offline; } - if (device.maxRetry !== 0 ) { - config['maxRetry'] = device.maxRetry; + if (device.maxRetry !== 0) { + deviceConfig['maxRetry'] = device.maxRetry; } if (device.webhook === true) { - config['webhook'] = device.webhook; + deviceConfig['webhook'] = device.webhook; } - if (device.connectionType) { - config['connectionType'] = device.connectionType; + if (device.connectionType !== '') { + deviceConfig['connectionType'] = device.connectionType; } - if (device.external !== undefined) { - config['external'] = device.external; + if (device.external === true) { + deviceConfig['external'] = device.external; } - if (device.mqttURL !== undefined) { - config['mqttURL'] = device.mqttURL; + if (device.mqttURL !== '') { + deviceConfig['mqttURL'] = device.mqttURL; } - if (device.maxRetries !== undefined) { - config['maxRetries'] = device.maxRetries; + if (device.maxRetries !== 0) { + deviceConfig['maxRetries'] = device.maxRetries; } - if (device.delayBetweenRetries !== undefined) { - config['delayBetweenRetries'] = device.delayBetweenRetries; + if (device.delayBetweenRetries !== 0) { + deviceConfig['delayBetweenRetries'] = device.delayBetweenRetries; } + let botConfig = {}; if (device.bot) { - config = device.bot; + botConfig = device.bot; } + let lockConfig = {}; if (device.lock) { - config = device.lock; + lockConfig = device.lock; } + let ceilinglightConfig = {}; if (device.ceilinglight) { - config = device.ceilinglight; + ceilinglightConfig = device.ceilinglight; } + let colorbulbConfig = {}; if (device.colorbulb) { - config = device.colorbulb; + colorbulbConfig = device.colorbulb; } + let contactConfig = {}; if (device.contact) { - config = device.contact; + contactConfig = device.contact; } + let motionConfig = {}; if (device.motion) { - config = device.motion; + motionConfig = device.motion; } + let curtainConfig = {}; if (device.curtain) { - config = device.curtain; + curtainConfig = device.curtain; } + let hubConfig = {}; if (device.hub) { - config = device.hub; + hubConfig = device.hub; } + let waterdetectorConfig = {}; if (device.waterdetector) { - config = device.waterdetector; + waterdetectorConfig = device.waterdetector; } + let humidifierConfig = {}; if (device.humidifier) { - config = device.humidifier; + humidifierConfig = device.humidifier; } + let meterConfig = {}; if (device.meter) { - config = device.meter; + meterConfig = device.meter; } + let iosensorConfig = {}; + if (device.iosensor) { + iosensorConfig = device.iosensor; + } + let striplightConfig = {}; if (device.striplight) { - config = device.striplight; + striplightConfig = device.striplight; } + let plugConfig = {}; if (device.plug) { - config = device.plug; + plugConfig = device.plug; } + let blindTiltConfig = {}; if (device.blindTilt) { if (device.blindTilt?.mode === undefined) { - config['mode'] = BlindTiltMappingMode.OnlyUp; - } else { - config['mode'] = device.blindTilt?.mode; - - config = device.blindTilt; + blindTiltConfig['mode'] = BlindTiltMappingMode.OnlyUp; } + blindTiltConfig = device.blindTilt; } + const config = Object.assign({}, deviceConfig, botConfig, curtainConfig, waterdetectorConfig, striplightConfig, plugConfig, iosensorConfig, + meterConfig, humidifierConfig, hubConfig, lockConfig, ceilinglightConfig, colorbulbConfig, contactConfig, motionConfig, blindTiltConfig); if (Object.entries(config).length !== 0) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); } @@ -613,7 +630,7 @@ export abstract class deviceBase { break; default: this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` - + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); } } From eb4788b6196b0e27f7ef606df066c403dd7631d3 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 06:37:37 -0500 Subject: [PATCH 39/73] only if version is available --- src/device/blindtilt.ts | 6 ++-- src/device/bot.ts | 62 ++++++++++++++++---------------- src/device/ceilinglight.ts | 14 ++++---- src/device/colorbulb.ts | 14 ++++---- src/device/contact.ts | 14 ++++---- src/device/curtain.ts | 14 ++++---- src/device/device.ts | 2 -- src/device/fan.ts | 28 ++++++++------- src/device/hub.ts | 18 +++++----- src/device/humidifier.ts | 14 ++++---- src/device/iosensor.ts | 14 ++++---- src/device/lightstrip.ts | 14 ++++---- src/device/lock.ts | 14 ++++---- src/device/meter.ts | 14 ++++---- src/device/meterplus.ts | 14 ++++---- src/device/motion.ts | 14 ++++---- src/device/plug.ts | 14 ++++---- src/device/robotvacuumcleaner.ts | 14 ++++---- src/device/waterdetector.ts | 14 ++++---- 19 files changed, 174 insertions(+), 138 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index f3a74e09..b2225367 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -263,7 +263,7 @@ export class BlindTilt extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` - + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); if (!this.device.blindTilt?.hide_lightsensor) { const set_minLux = await this.minLux(); @@ -436,12 +436,14 @@ export class BlindTilt extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(this.accessory.context.FirmwareRevision); + } } async refreshStatus(): Promise { diff --git a/src/device/bot.ts b/src/device/bot.ts index a7f9a052..a50f7240 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -519,12 +519,14 @@ export class Bot extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** @@ -1065,36 +1067,36 @@ export class Bot extends deviceBase { async removeOutletService(accessory: PlatformAccessory): Promise { // If outletService still present, then remove first if (this.Outlet?.Service) { - this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); - accessory.removeService(this.Outlet!.Service); + this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); + accessory.removeService(this.Outlet!.Service); } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If garageDoorService still present, then remove first if (this.GarageDoor?.Service) { - this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - accessory.removeService(this.GarageDoor!.Service); + this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); + accessory.removeService(this.GarageDoor!.Service); } } async removeDoorService(accessory: PlatformAccessory): Promise { // If doorService still present, then remove first if (this.Door?.Service) { - this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); - accessory.removeService(this.Door!.Service); + this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); + accessory.removeService(this.Door!.Service); } } async removeLockService(accessory: PlatformAccessory): Promise { // If lockService still present, then remove first if (this.Lock?.Service) { - this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.Lock!.Service); + this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); + accessory.removeService(this.Lock!.Service); } } @@ -1110,27 +1112,27 @@ export class Bot extends deviceBase { async removeFanService(accessory: PlatformAccessory): Promise { // If fanService still present, then remove first if (this.Fan?.Service) { - this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); - accessory.removeService(this.Fan!.Service); + this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); + accessory.removeService(this.Fan!.Service); } } async removeWindowService(accessory: PlatformAccessory): Promise { // If windowService still present, then remove first if (this.Window?.Service) { - this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); - accessory.removeService(this.Window!.Service); + this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); + accessory.removeService(this.Window!.Service); } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If windowCoveringService still present, then remove first if (this.WindowCovering?.Service) { - this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - accessory.removeService(this.WindowCovering!.Service); + this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); + accessory.removeService(this.WindowCovering!.Service); } } @@ -1146,9 +1148,9 @@ export class Bot extends deviceBase { async removeSwitchService(accessory: PlatformAccessory): Promise { // If switchService still present, then remove first if (this.Switch?.Service) { - this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; - this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); - accessory.removeService(this.Switch!.Service); + this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); + accessory.removeService(this.Switch!.Service); } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index b75844cc..09b7da99 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -288,12 +288,14 @@ export class CeilingLight extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index bd1ec82b..813656d8 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -349,12 +349,14 @@ export class ColorBulb extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/contact.ts b/src/device/contact.ts index 908efec4..939d9ef0 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -279,12 +279,14 @@ export class Contact extends deviceBase { + `StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 7cc933d2..ac732d08 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -464,12 +464,14 @@ export class Curtain extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } async refreshStatus(): Promise { diff --git a/src/device/device.ts b/src/device/device.ts index 9841e2ac..82cdcafc 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -559,8 +559,6 @@ export abstract class deviceBase { async statusCode(statusCode: number): Promise { switch (this.device.deviceType) { case this.device.hubDeviceId: - statusCode = 161; - break; case '000000000000': statusCode = 161; break; diff --git a/src/device/fan.ts b/src/device/fan.ts index 1ebf6901..a575e611 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -119,12 +119,14 @@ export class Fan extends deviceBase { this.Battery.BatteryLevel = battery; // FirmwareRevision - this.accessory.context.FirmwareRevision = version; + if (version) { + this.accessory.context.FirmwareRevision = version; this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(this.accessory.context.FirmwareRevision); + } this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -202,12 +204,14 @@ export class Fan extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } @@ -475,7 +479,7 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`); } // RotationSpeed - if(this.Fan.RotationSpeed === undefined) { + if (this.Fan.RotationSpeed === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`); } else { this.accessory.context.RotationSpeed = this.Fan.RotationSpeed; @@ -483,7 +487,7 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`); } // SwingMode - if(this.Fan.SwingMode === undefined) { + if (this.Fan.SwingMode === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`); } else { this.accessory.context.SwingMode = this.Fan.SwingMode; @@ -491,7 +495,7 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`); } // BateryLevel - if(this.Battery.BatteryLevel === undefined) { + if (this.Battery.BatteryLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; @@ -499,7 +503,7 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // ChargingState - if(this.Battery.ChargingState === undefined) { + if (this.Battery.ChargingState === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`); } else { this.accessory.context.ChargingState = this.Battery.ChargingState; @@ -507,7 +511,7 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ChargingState: ${this.Battery.ChargingState}`); } // StatusLowBattery - if(this.Battery.StatusLowBattery === undefined) { + if (this.Battery.StatusLowBattery === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; diff --git a/src/device/hub.ts b/src/device/hub.ts index 291544e6..0a2d55c1 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -157,12 +157,12 @@ export class Hub extends deviceBase { const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; if (context.scale !== 'CELCIUS' && device.hub?.convertUnitTo === undefined) { this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` - + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`); + + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(scale, temperature, humidity, lightLevel) = ' + - `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); + '(scale, temperature, humidity, lightLevel) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); if (!this.device.hub?.hide_humidity) { this.HumiditySensor!.CurrentRelativeHumidity = humidity; } @@ -217,12 +217,14 @@ export class Hub extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(this.accessory.context.FirmwareRevision); + } } async refreshStatus(): Promise { @@ -278,7 +280,7 @@ export class Hub extends deviceBase { if (!this.device.hub?.hide_humidity) { if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - +` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { if (this.device.mqttURL) { mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`); @@ -308,7 +310,7 @@ export class Hub extends deviceBase { this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature; this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); + + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`); } } @@ -316,7 +318,7 @@ export class Hub extends deviceBase { if (!this.device.hub?.hide_lightsensor) { if (this.LightSensor!.CurrentAmbientLightLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } else { if (this.device.mqttURL) { mqttmessage.push(`"light": ${this.LightSensor!.CurrentAmbientLightLevel}`); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 86c55751..08a43940 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -269,12 +269,14 @@ export class Humidifier extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } async refreshStatus(): Promise { diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 2fecfeec..5d7d1827 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -228,12 +228,14 @@ export class IOSensor extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 35e13977..4a6ead21 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -326,12 +326,14 @@ export class StripLight extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/lock.ts b/src/device/lock.ts index 9ec56bb4..5a9a21fc 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -256,12 +256,14 @@ export class Lock extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/meter.ts b/src/device/meter.ts index 2ca0ddfc..8e9dbce3 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -224,12 +224,14 @@ export class Meter extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index f3062f5c..a3dd20fb 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -225,12 +225,14 @@ export class MeterPlus extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/motion.ts b/src/device/motion.ts index a0196ee7..d5180382 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -200,12 +200,14 @@ export class Motion extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/plug.ts b/src/device/plug.ts index bb9f7abf..728969c3 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -130,12 +130,14 @@ export class Plug extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 6990af96..f03c7cab 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -199,12 +199,14 @@ export class RobotVacuumCleaner extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } /** diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 28413570..1c577d56 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -166,12 +166,14 @@ export class WaterDetector extends deviceBase { } // FirmwareRevision - this.accessory.context.FirmwareRevision = deviceStatus.body.version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + if (deviceStatus.body.version) { + this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.FirmwareRevision); + } } async refreshStatus(): Promise { From 642e16648a8d00a5beda7b2102fd50281206f884 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 06:42:34 -0500 Subject: [PATCH 40/73] Update device.ts --- src/device/device.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 82cdcafc..1a432b11 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -71,9 +71,7 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(accessory.context.FirmwareRevision); + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); } async getDeviceLogSettings(device: device & devicesConfig): Promise { From cc177a62da80f1a4ee6a7234e8bdd49d2db942b9 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 06:57:03 -0500 Subject: [PATCH 41/73] Update device.ts --- src/device/device.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 1a432b11..3e075423 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -70,8 +70,7 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision); + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId); } async getDeviceLogSettings(device: device & devicesConfig): Promise { From ee1c17620dbf20a1625f1a219a208e4f9bf4a43e Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 07:07:41 -0500 Subject: [PATCH 42/73] more firmware version fixes --- src/device/blindtilt.ts | 6 +- src/device/bot.ts | 6 +- src/device/ceilinglight.ts | 6 +- src/device/colorbulb.ts | 6 +- src/device/contact.ts | 6 +- src/device/curtain.ts | 6 +- src/device/device.ts | 13 ++-- src/device/fan.ts | 12 +-- src/device/hub.ts | 6 +- src/device/humidifier.ts | 6 +- src/device/iosensor.ts | 6 +- src/device/lightstrip.ts | 6 +- src/device/lock.ts | 6 +- src/device/meter.ts | 6 +- src/device/meterplus.ts | 6 +- src/device/motion.ts | 6 +- src/device/plug.ts | 6 +- src/device/robotvacuumcleaner.ts | 6 +- src/device/waterdetector.ts | 6 +- src/homebridge-ui/public/index.html | 4 +- src/irdevice/irdevice.ts | 12 +-- src/platform.ts | 110 ++++++++++++++-------------- 22 files changed, 128 insertions(+), 125 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index b2225367..450e2485 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -437,12 +437,12 @@ export class BlindTilt extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/bot.ts b/src/device/bot.ts index a50f7240..c3734983 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -520,12 +520,12 @@ export class Bot extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 09b7da99..8851a3c5 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -289,12 +289,12 @@ export class CeilingLight extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 813656d8..3d21aed4 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -350,12 +350,12 @@ export class ColorBulb extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/contact.ts b/src/device/contact.ts index 939d9ef0..23900fb4 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -280,12 +280,12 @@ export class Contact extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index ac732d08..b9e68ccd 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -465,12 +465,12 @@ export class Curtain extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/device.ts b/src/device/device.ts index 3e075423..7a50da11 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -70,7 +70,10 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId); + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(accessory.context.version); } async getDeviceLogSettings(device: device & devicesConfig): Promise { @@ -544,12 +547,12 @@ export abstract class deviceBase { accessory.context.deviceType = device.deviceType; if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } else if (device.firmware === undefined && device.version === undefined && accessory.context.FirmwareRevision === undefined) { + accessory.context.version = device.firmware; + } else if (device.firmware === undefined && device.version === undefined && accessory.context.version === undefined) { device.version = this.platform.version; - accessory.context.FirmwareRevision = this.platform.version; + accessory.context.version = this.platform.version; } else { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } } diff --git a/src/device/fan.ts b/src/device/fan.ts index a575e611..358ed52d 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -120,12 +120,12 @@ export class Fan extends deviceBase { // FirmwareRevision if (version) { - this.accessory.context.FirmwareRevision = version; + this.accessory.context.version = version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -205,12 +205,12 @@ export class Fan extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/hub.ts b/src/device/hub.ts index 0a2d55c1..fa9fb37e 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -218,12 +218,12 @@ export class Hub extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 08a43940..28087ff5 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -270,12 +270,12 @@ export class Humidifier extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 5d7d1827..1994a9a8 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -229,12 +229,12 @@ export class IOSensor extends deviceBase { } // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 4a6ead21..fa51bcc2 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -327,12 +327,12 @@ export class StripLight extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/lock.ts b/src/device/lock.ts index 5a9a21fc..29a5b4a6 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -257,12 +257,12 @@ export class Lock extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 8e9dbce3..977b8333 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -225,12 +225,12 @@ export class Meter extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index a3dd20fb..b5426585 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -226,12 +226,12 @@ export class MeterPlus extends deviceBase { } // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/motion.ts b/src/device/motion.ts index d5180382..1730cbee 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -201,12 +201,12 @@ export class Motion extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/plug.ts b/src/device/plug.ts index 728969c3..0326b86a 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -131,12 +131,12 @@ export class Plug extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index f03c7cab..6e6c7d1f 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -200,12 +200,12 @@ export class RobotVacuumCleaner extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 1c577d56..971b26fd 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -167,12 +167,12 @@ export class WaterDetector extends deviceBase { // FirmwareRevision if (deviceStatus.body.version) { - this.accessory.context.FirmwareRevision = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.FirmwareRevision); + .updateValue(this.accessory.context.version); } } diff --git a/src/homebridge-ui/public/index.html b/src/homebridge-ui/public/index.html index 0c9f7d7a..e50467cb 100644 --- a/src/homebridge-ui/public/index.html +++ b/src/homebridge-ui/public/index.html @@ -60,7 +60,7 @@ Firmware Version - + Device Type @@ -162,7 +162,7 @@
Help/About
document.getElementById('displayName').innerHTML = thisAcc.displayName; document.getElementById('deviceID').innerHTML = context.deviceID; document.getElementById('model').innerHTML = context.model; - document.getElementById('firmwareRevision').innerHTML = context.firmwareRevision || 'N/A'; + document.getElementById('version').innerHTML = context.version || 'N/A'; document.getElementById('deviceType').innerHTML = context.deviceType; document.getElementById('connectionType').innerHTML = context.connectionType; document.getElementById('deviceTable').style.display = 'inline-table'; diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index a9917200..00fe0aaf 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -43,9 +43,9 @@ export abstract class irdeviceBase { .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.firmware) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(accessory.context.FirmwareRevision); + .updateValue(accessory.context.firmware); } async getDeviceLogSettings(device: irdevice & irDevicesConfig): Promise { @@ -326,12 +326,12 @@ export abstract class irdeviceBase { accessory.context.deviceId = device.deviceId; accessory.context.remoteType = device.remoteType; if (device.firmware) { - accessory.context.FirmwareRevision = device.firmware; - } else if (device.firmware === undefined || accessory.context.FirmwareRevision === undefined) { + accessory.context.firmware = device.firmware; + } else if (device.firmware === undefined || accessory.context.firmware === undefined) { device.firmware = this.platform.version; - accessory.context.FirmwareRevision = device.firmware; + accessory.context.firmware = device.firmware; } else { - accessory.context.FirmwareRevision = 'Unknown'; + accessory.context.firmware = 'Unknown'; } } diff --git a/src/platform.ts b/src/platform.ts index 89e4a5da..1fb7bb30 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -846,7 +846,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -874,7 +874,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -906,7 +906,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -934,11 +934,11 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); - // accessory.context.FirmwareRevision = findaccessories.accessoryAttribute.softwareRevision; + // accessory.context.version = findaccessories.accessoryAttribute.softwareRevision; // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new Bot(this, accessory, device); @@ -967,7 +967,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -995,7 +995,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1028,7 +1028,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1056,7 +1056,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1089,7 +1089,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1117,7 +1117,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1149,7 +1149,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1177,7 +1177,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1209,7 +1209,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1237,7 +1237,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1269,7 +1269,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1297,7 +1297,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1329,7 +1329,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1357,7 +1357,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1389,7 +1389,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1430,7 +1430,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1462,7 +1462,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1503,7 +1503,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1535,7 +1535,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1563,7 +1563,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1595,7 +1595,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1623,7 +1623,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1655,7 +1655,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1683,7 +1683,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1715,7 +1715,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1743,7 +1743,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1775,7 +1775,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1803,7 +1803,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1835,7 +1835,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1863,7 +1863,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1895,7 +1895,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1923,7 +1923,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1953,7 +1953,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1978,7 +1978,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2009,7 +2009,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2037,7 +2037,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2069,7 +2069,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2097,7 +2097,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2129,7 +2129,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2157,7 +2157,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2189,7 +2189,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2217,7 +2217,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2249,7 +2249,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2277,7 +2277,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2309,7 +2309,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2337,7 +2337,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2369,7 +2369,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2397,7 +2397,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2429,7 +2429,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.FirmwareRevision = device.version; + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2457,7 +2457,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.FirmwareRevision = device.version; + accessory.context.version = device.version; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; From 4b5e5ada9ae24eadeeab74e22340299c846607c8 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 07:15:41 -0500 Subject: [PATCH 43/73] logging for firmware version --- src/device/blindtilt.ts | 3 ++- src/device/bot.ts | 3 ++- src/device/ceilinglight.ts | 3 ++- src/device/colorbulb.ts | 3 ++- src/device/contact.ts | 3 ++- src/device/curtain.ts | 3 ++- src/device/fan.ts | 5 +++-- src/device/hub.ts | 3 ++- src/device/humidifier.ts | 3 ++- src/device/iosensor.ts | 3 ++- src/device/lightstrip.ts | 3 ++- src/device/lock.ts | 3 ++- src/device/meter.ts | 3 ++- src/device/meterplus.ts | 3 ++- src/device/motion.ts | 3 ++- src/device/plug.ts | 3 ++- src/device/robotvacuumcleaner.ts | 3 ++- src/device/waterdetector.ts | 3 ++- 18 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 450e2485..4835e1fc 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -435,7 +435,8 @@ export class BlindTilt extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/bot.ts b/src/device/bot.ts index c3734983..79b9182f 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -518,7 +518,8 @@ export class Bot extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 8851a3c5..56532cb4 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -287,7 +287,8 @@ export class CeilingLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 3d21aed4..a9d049b3 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -348,7 +348,8 @@ export class ColorBulb extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/contact.ts b/src/device/contact.ts index 23900fb4..275131fc 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -278,7 +278,8 @@ export class Contact extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, ` + `StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/curtain.ts b/src/device/curtain.ts index b9e68ccd..fdf77fa3 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -463,7 +463,8 @@ export class Curtain extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/fan.ts b/src/device/fan.ts index 358ed52d..48aed2f1 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -118,7 +118,7 @@ export class Fan extends deviceBase { // BatteryLevel this.Battery.BatteryLevel = battery; - // FirmwareRevision + // Firmware Version if (version) { this.accessory.context.version = version; this.accessory @@ -203,7 +203,8 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/hub.ts b/src/device/hub.ts index fa9fb37e..f0c2c6e5 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -216,7 +216,8 @@ export class Hub extends deviceBase { } } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 28087ff5..a4d4321f 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -268,7 +268,8 @@ export class Humidifier extends deviceBase { } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 1994a9a8..612f78aa 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -227,7 +227,8 @@ export class IOSensor extends deviceBase { this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index fa51bcc2..c0920235 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -325,7 +325,8 @@ export class StripLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/lock.ts b/src/device/lock.ts index 29a5b4a6..e52603f4 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -255,7 +255,8 @@ export class Lock extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/meter.ts b/src/device/meter.ts index 977b8333..b1255381 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -223,7 +223,8 @@ export class Meter extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index b5426585..bc7de4fb 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -224,7 +224,8 @@ export class MeterPlus extends deviceBase { this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/motion.ts b/src/device/motion.ts index 1730cbee..fda1000b 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -199,7 +199,8 @@ export class Motion extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/plug.ts b/src/device/plug.ts index 0326b86a..3db082f4 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -129,7 +129,8 @@ export class Plug extends deviceBase { } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 6e6c7d1f..846780dc 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -198,7 +198,8 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 971b26fd..a63edaf3 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -165,7 +165,8 @@ export class WaterDetector extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LeakDetected: ${this.LeakSensor!.LeakDetected}`); } - // FirmwareRevision + // Firmware Version + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { this.accessory.context.version = deviceStatus.body.version; this.accessory From da3b647f6a5ae9d60e57a4ca5d3a44a5339d05c4 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 07:27:40 -0500 Subject: [PATCH 44/73] version --- src/device/device.ts | 3 +-- src/platform.ts | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 7a50da11..5bfc72c7 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -72,8 +72,7 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(accessory.context.version); + .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version); } async getDeviceLogSettings(device: device & devicesConfig): Promise { diff --git a/src/platform.ts b/src/platform.ts index 1fb7bb30..9b35fd99 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1654,6 +1654,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; + this.warnLog(`${existingAccessory.displayName} version: ${device.version} firmware: ${device.firmware}`); if (device.version && !device.firmware) { existingAccessory.context.version = device.version; } From 3a3fdefc24e143e6575b32846d159e02ba051647 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 07:37:37 -0500 Subject: [PATCH 45/73] Update device.ts --- src/device/device.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 5bfc72c7..2d8f97ee 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -55,13 +55,15 @@ export abstract class deviceBase { this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - this.getDeviceLogSettings(device); - this.getDeviceRefreshRateSettings(device); - this.getDeviceRetry(device); - this.getDeviceConfigSettings(device); - this.getDeviceContext(accessory, device); - this.setupMqtt(device); - this.scan(device); + (async () => { + await this.getDeviceLogSettings(device); + await this.getDeviceRefreshRateSettings(device); + await this.getDeviceRetry(device); + await this.getDeviceConfigSettings(device); + await this.getDeviceContext(accessory, device); + await this.setupMqtt(device); + await this.scan(device); + })(); // Set accessory information accessory @@ -71,8 +73,8 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) - .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version); + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, device.version || accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, device.version || accessory.context.version); } async getDeviceLogSettings(device: device & devicesConfig): Promise { From aece274a3cff9fbf9829dbc5b4b686bef35fbc52 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 07:51:39 -0500 Subject: [PATCH 46/73] version revision --- src/device/blindtilt.ts | 2 +- src/device/bot.ts | 2 +- src/device/ceilinglight.ts | 2 +- src/device/colorbulb.ts | 2 +- src/device/contact.ts | 2 +- src/device/curtain.ts | 2 +- src/device/device.ts | 7 +- src/device/fan.ts | 2 +- src/device/hub.ts | 2 +- src/device/humidifier.ts | 2 +- src/device/iosensor.ts | 2 +- src/device/lightstrip.ts | 2 +- src/device/lock.ts | 2 +- src/device/meter.ts | 2 +- src/device/meterplus.ts | 2 +- src/device/motion.ts | 2 +- src/device/plug.ts | 2 +- src/device/robotvacuumcleaner.ts | 2 +- src/device/waterdetector.ts | 2 +- src/platform.ts | 108 +++++++++++++++---------------- 20 files changed, 76 insertions(+), 75 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 4835e1fc..02d8c518 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -438,7 +438,7 @@ export class BlindTilt extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/bot.ts b/src/device/bot.ts index 79b9182f..146224b4 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -521,7 +521,7 @@ export class Bot extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 56532cb4..f5ecf5b3 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -290,7 +290,7 @@ export class CeilingLight extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index a9d049b3..85134a64 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -351,7 +351,7 @@ export class ColorBulb extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/contact.ts b/src/device/contact.ts index 275131fc..35799afc 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -281,7 +281,7 @@ export class Contact extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/curtain.ts b/src/device/curtain.ts index fdf77fa3..d7d4ca7a 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -466,7 +466,7 @@ export class Curtain extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/device.ts b/src/device/device.ts index 2d8f97ee..a76d6585 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -73,8 +73,8 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, device.version || accessory.context.version) - .setCharacteristic(this.hap.Characteristic.HardwareRevision, device.version || accessory.context.version); + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, device.version?.toString() || accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, device.version?.toString() || accessory.context.version); } async getDeviceLogSettings(device: device & devicesConfig): Promise { @@ -553,8 +553,9 @@ export abstract class deviceBase { device.version = this.platform.version; accessory.context.version = this.platform.version; } else { - accessory.context.version = device.version; + accessory.context.version = device.version?.toString; } + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Context: ${JSON.stringify(accessory.context)}`); } async statusCode(statusCode: number): Promise { diff --git a/src/device/fan.ts b/src/device/fan.ts index 48aed2f1..89161e09 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -206,7 +206,7 @@ export class Fan extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/hub.ts b/src/device/hub.ts index f0c2c6e5..1c70f2d9 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -219,7 +219,7 @@ export class Hub extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index a4d4321f..3755569e 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -271,7 +271,7 @@ export class Humidifier extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 612f78aa..b1e58d50 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -230,7 +230,7 @@ export class IOSensor extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index c0920235..39331da7 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -328,7 +328,7 @@ export class StripLight extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/lock.ts b/src/device/lock.ts index e52603f4..89d19bdc 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -258,7 +258,7 @@ export class Lock extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/meter.ts b/src/device/meter.ts index b1255381..3671c296 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -226,7 +226,7 @@ export class Meter extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index bc7de4fb..563f34f0 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -227,7 +227,7 @@ export class MeterPlus extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/motion.ts b/src/device/motion.ts index fda1000b..a181796c 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -202,7 +202,7 @@ export class Motion extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/plug.ts b/src/device/plug.ts index 3db082f4..52221667 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -132,7 +132,7 @@ export class Plug extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 846780dc..18b69bd0 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -201,7 +201,7 @@ export class RobotVacuumCleaner extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index a63edaf3..a271d87d 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -168,7 +168,7 @@ export class WaterDetector extends deviceBase { // Firmware Version this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version; + this.accessory.context.version = deviceStatus.body.version.toString(); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/platform.ts b/src/platform.ts index 9b35fd99..416c033c 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -846,7 +846,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -874,7 +874,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -906,7 +906,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -934,7 +934,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -967,7 +967,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -995,7 +995,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1028,7 +1028,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1056,7 +1056,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1089,7 +1089,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1117,7 +1117,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1149,7 +1149,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1177,7 +1177,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1209,7 +1209,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1237,7 +1237,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1269,7 +1269,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1297,7 +1297,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1329,7 +1329,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1357,7 +1357,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1389,7 +1389,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1430,7 +1430,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1462,7 +1462,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1503,7 +1503,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1535,7 +1535,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1563,7 +1563,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1595,7 +1595,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1623,7 +1623,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1656,7 +1656,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.displayName = device.configDeviceName || device.deviceName; this.warnLog(`${existingAccessory.displayName} version: ${device.version} firmware: ${device.firmware}`); if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1684,7 +1684,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1716,7 +1716,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1744,7 +1744,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1776,7 +1776,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1804,7 +1804,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1836,7 +1836,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1864,7 +1864,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1896,7 +1896,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1924,7 +1924,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1954,7 +1954,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1979,7 +1979,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2010,7 +2010,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2038,7 +2038,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2070,7 +2070,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2098,7 +2098,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2130,7 +2130,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2158,7 +2158,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2190,7 +2190,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2218,7 +2218,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2250,7 +2250,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2278,7 +2278,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2310,7 +2310,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2338,7 +2338,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2370,7 +2370,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2398,7 +2398,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2430,7 +2430,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version; + existingAccessory.context.version = device.version.toString(); } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2458,7 +2458,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; if (device.version && !device.firmware) { - accessory.context.version = device.version; + accessory.context.version = device.version.toString(); } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; From 798f12760c20b50bcd5b9004bf2f6d0662aa34d8 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 09:43:48 -0500 Subject: [PATCH 47/73] fix statuscode issue --- src/device/device.ts | 12 ++++++++---- src/device/humidifier.ts | 21 ++++++++++----------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index a76d6585..8e18990a 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -559,10 +559,14 @@ export abstract class deviceBase { } async statusCode(statusCode: number): Promise { - switch (this.device.deviceType) { - case this.device.hubDeviceId: - case '000000000000': - statusCode = 161; + switch (statusCode) { + case 171: + switch (this.device.deviceId) { + case this.device.hubDeviceId: + case '000000000000': + statusCode = 161; + break; + } break; } switch (statusCode) { diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 3755569e..2e640f78 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -5,6 +5,7 @@ import { SwitchBotPlatform } from '../platform.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +import { convertUnits } from '../utils.js'; /** * Platform Accessory @@ -148,20 +149,18 @@ export class Humidifier extends deviceBase { this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity } = context; - const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + const { temperature, humidity } = context; + const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + + `Webhook:(${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; - if (!this.device.humidifier?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = temperature; - } - this.updateHomeKitCharacteristics(); + this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; + if (!this.device.humidifier?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); } + this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); From 1b0eb0ca0bcb9fbf7e3380ac16137e2246b079ef Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 11:16:48 -0500 Subject: [PATCH 48/73] stringify --- src/device/colorbulb.ts | 6 +++--- src/device/curtain.ts | 2 +- src/device/fan.ts | 2 +- src/device/humidifier.ts | 6 +++--- src/device/lightstrip.ts | 6 +++--- src/device/lock.ts | 2 +- src/device/plug.ts | 2 +- src/device/robotvacuumcleaner.ts | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 85134a64..3f34c9ca 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -755,7 +755,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -801,7 +801,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -841,7 +841,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/curtain.ts b/src/device/curtain.ts index d7d4ca7a..7ba11856 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -667,7 +667,7 @@ export class Curtain extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/fan.ts b/src/device/fan.ts index 89161e09..7cb75c76 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -412,7 +412,7 @@ export class Fan extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 2e640f78..2f49b063 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -445,7 +445,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -500,7 +500,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -546,7 +546,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 39331da7..682d6fef 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -630,7 +630,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -688,7 +688,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -732,7 +732,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/lock.ts b/src/device/lock.ts index 89d19bdc..b4d0c65d 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -452,7 +452,7 @@ export class Lock extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/plug.ts b/src/device/plug.ts index 52221667..9a55f5d8 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -336,7 +336,7 @@ export class Plug extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 18b69bd0..d2a42042 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -482,7 +482,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${deviceStatus} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); From a7b7909acca8696682bc69c3bcf6f95aa6144aae Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 11:21:22 -0500 Subject: [PATCH 49/73] Update device.ts --- src/device/device.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/device.ts b/src/device/device.ts index 8e18990a..534a7adb 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -63,7 +63,7 @@ export abstract class deviceBase { await this.getDeviceContext(accessory, device); await this.setupMqtt(device); await this.scan(device); - })(); + }); // Set accessory information accessory From 908a1964ea6243fcbe4389778304c03a7f4cb5a7 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 11:23:37 -0500 Subject: [PATCH 50/73] Update device.ts --- src/device/device.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 534a7adb..0840327f 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -55,15 +55,13 @@ export abstract class deviceBase { this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - (async () => { - await this.getDeviceLogSettings(device); - await this.getDeviceRefreshRateSettings(device); - await this.getDeviceRetry(device); - await this.getDeviceConfigSettings(device); - await this.getDeviceContext(accessory, device); - await this.setupMqtt(device); - await this.scan(device); - }); + this.getDeviceLogSettings(device); + this.getDeviceRefreshRateSettings(device); + this.getDeviceRetry(device); + this.getDeviceConfigSettings(device); + this.getDeviceContext(accessory, device); + this.setupMqtt(device); + this.scan(device); // Set accessory information accessory From 9f15d34db25de0595f702a2dce44325490023179 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 13:10:32 -0500 Subject: [PATCH 51/73] Update device.ts --- src/device/device.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 0840327f..c28ccb14 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -70,9 +70,7 @@ export abstract class deviceBase { .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, device.version?.toString() || accessory.context.version) - .setCharacteristic(this.hap.Characteristic.HardwareRevision, device.version?.toString() || accessory.context.version); + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId); } async getDeviceLogSettings(device: device & devicesConfig): Promise { @@ -553,6 +551,14 @@ export abstract class deviceBase { } else { accessory.context.version = device.version?.toString; } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${accessory.context.version}`); + if (accessory.context.version) { + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.version); + } this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Context: ${JSON.stringify(accessory.context)}`); } From 0d4474a7b9fa22f79e4b16b8418f098368146388 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 16:34:14 -0500 Subject: [PATCH 52/73] Update platform.ts --- src/platform.ts | 215 ++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 108 deletions(-) diff --git a/src/platform.ts b/src/platform.ts index 416c033c..79e36457 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -845,8 +845,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -873,8 +873,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -905,8 +905,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -933,8 +933,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -966,8 +966,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -994,8 +994,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1027,8 +1027,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1055,8 +1055,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1088,8 +1088,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1116,8 +1116,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1148,8 +1148,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1176,8 +1176,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1208,8 +1208,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1236,8 +1236,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1268,8 +1268,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1296,8 +1296,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1328,8 +1328,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1356,8 +1356,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1388,8 +1388,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1429,8 +1429,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1461,8 +1461,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1502,8 +1502,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1534,8 +1534,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1562,8 +1562,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1594,8 +1594,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1622,8 +1622,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1654,9 +1654,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - this.warnLog(`${existingAccessory.displayName} version: ${device.version} firmware: ${device.firmware}`); - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1683,8 +1682,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1715,8 +1714,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1743,8 +1742,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1775,8 +1774,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1803,8 +1802,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1835,8 +1834,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1863,8 +1862,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1895,8 +1894,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.deviceType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1923,8 +1922,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.deviceType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `SwitchBot: ${device.deviceType}`; accessory.context.connectionType = await this.connectionType(device); @@ -1954,7 +1953,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + existingAccessory.context.version = device.version; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -1978,8 +1977,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2009,8 +2008,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2037,8 +2036,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2069,8 +2068,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2097,8 +2096,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2129,8 +2128,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2157,8 +2156,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2189,8 +2188,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2217,8 +2216,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2249,8 +2248,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2277,8 +2276,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2309,8 +2308,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2337,8 +2336,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2369,8 +2368,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2397,8 +2396,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; @@ -2429,8 +2428,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { existingAccessory.context.model = device.remoteType; existingAccessory.context.deviceID = device.deviceId; existingAccessory.displayName = device.configDeviceName || device.deviceName; - if (device.version && !device.firmware) { - existingAccessory.context.version = device.version.toString(); + if (device.firmware) { + existingAccessory.context.version = device.firmware; } existingAccessory.context.deviceType = `IR: ${device.remoteType}`; this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); @@ -2457,8 +2456,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { accessory.context.device = device; accessory.context.model = device.remoteType; accessory.context.deviceID = device.deviceId; - if (device.version && !device.firmware) { - accessory.context.version = device.version.toString(); + if (device.firmware) { + accessory.context.version = device.firmware; } accessory.context.deviceType = `IR: ${device.remoteType}`; accessory.context.connectionType = device.connectionType; From 1109f484306ff90de749b410d650ca67fd3d0bb5 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 18:01:05 -0500 Subject: [PATCH 53/73] see if this fixes the version --- eslint.config.js | 87 ++++ package-lock.json | 771 ++++++++++++++----------------- package.json | 8 +- src/device/blindtilt.ts | 5 +- src/device/bot.ts | 5 +- src/device/ceilinglight.ts | 5 +- src/device/colorbulb.ts | 5 +- src/device/contact.ts | 5 +- src/device/curtain.ts | 5 +- src/device/device.ts | 3 +- src/device/fan.ts | 5 +- src/device/hub.ts | 5 +- src/device/humidifier.ts | 5 +- src/device/iosensor.ts | 5 +- src/device/lightstrip.ts | 5 +- src/device/lock.ts | 5 +- src/device/meter.ts | 5 +- src/device/meterplus.ts | 5 +- src/device/motion.ts | 5 +- src/device/plug.ts | 5 +- src/device/robotvacuumcleaner.ts | 5 +- src/device/waterdetector.ts | 5 +- 22 files changed, 498 insertions(+), 461 deletions(-) create mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..7b9e3315 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,87 @@ +export default [ + { + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" // uses the recommended rules from the @typescript-eslint/eslint-plugin + ], + "parserOptions": { + "ecmaVersion": 2021, + "sourceType": "module" + }, + "ignorePatterns": [ + "dist" + ], + "rules": { + "quotes": [ + "warn", + "single" + ], + "indent": [ + "warn", + 2, + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "warn", + "unix" + ], + "semi": [ + "warn", + "always" + ], + "comma-dangle": [ + "warn", + "always-multiline" + ], + "dot-notation": "off", + "eqeqeq": "warn", + "curly": [ + "warn", + "all" + ], + "brace-style": [ + "warn" + ], + "prefer-arrow-callback": [ + "warn" + ], + "max-len": [ + "warn", + 150 + ], + "no-console": [ + "warn" + ], // use the provided Homebridge log method instead + "no-non-null-assertion": [ + "off" + ], + "comma-spacing": [ + "error" + ], + "no-multi-spaces": [ + "warn", + { + "ignoreEOLComments": true + } + ], + "no-trailing-spaces": [ + "warn" + ], + "lines-between-class-members": [ + "warn", + "always", + { + "exceptAfterSingleLine": true + } + ], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off" + } + } +]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a7d7f18d..358cdab4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,13 +24,13 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", - "undici": "^6.16.1" + "undici": "^6.17.0" }, "devDependencies": { "@types/node": "^20.12.12", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", - "eslint": "^8.57.0", + "@typescript-eslint/eslint-plugin": "^8.0.0-alpha.13", + "@typescript-eslint/parser": "^8.0.0-alpha.13", + "eslint": "^9.3.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", @@ -47,15 +47,6 @@ "node-switchbot": "2.1.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@abandonware/bluetooth-hci-socket": { "version": "0.5.3-12", "resolved": "https://registry.npmjs.org/@abandonware/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.3-12.tgz", @@ -153,15 +144,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -169,7 +160,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -198,12 +189,12 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@fastify/accept-negotiator": { @@ -227,15 +218,15 @@ } }, "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -330,9 +321,9 @@ } }, "node_modules/@fastify/middie/node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true }, "node_modules/@fastify/multipart": { @@ -463,12 +454,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -517,6 +508,19 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1007,9 +1011,9 @@ } }, "node_modules/@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "devOptional": true, "dependencies": { "semver": "^7.3.5" @@ -1062,16 +1066,16 @@ } }, "node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", - "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", "dev": true, "dependencies": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" }, "bin": { - "installed-package-contents": "lib/index.js" + "installed-package-contents": "bin/index.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -2005,9 +2009,9 @@ } }, "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, "node_modules/@szmarczak/http-timer": { @@ -2098,6 +2102,12 @@ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", @@ -2116,6 +2126,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/semver-utils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", @@ -2123,9 +2139,9 @@ "dev": true }, "node_modules/@types/validator": { - "version": "13.11.9", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", - "integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==", + "version": "13.11.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz", + "integrity": "sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==", "dev": true }, "node_modules/@types/w3c-web-usb": { @@ -2135,31 +2151,33 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", - "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.13.tgz", + "integrity": "sha512-FQeu2HGVZ6wDAn2m6MxpS+or7LyBEjjCXnFv79aCJtcJnxtgDQa0po88GHZv1tZwzIsgxQ3bnbz4vNgbPisMbA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/type-utils": "7.9.0", - "@typescript-eslint/utils": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.13", + "@typescript-eslint/type-utils": "8.0.0-alpha.13", + "@typescript-eslint/utils": "8.0.0-alpha.13", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", + "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", + "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2168,26 +2186,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.13.tgz", + "integrity": "sha512-CWFhVzrA6n7OeTHmvtxxUy+BSvUwHl8BcT6QsVi87MGcGQpLQZ1TeU0kYoIISkzFojoULF5q0de9hP/FbANi/g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.13", + "@typescript-eslint/types": "8.0.0-alpha.13", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2196,16 +2214,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.13.tgz", + "integrity": "sha512-5XA+tfDumd6Y5GWVFMF2810HB8GIijMawdLgMptSeN3CeOfxQB1J3EBtmbgSWuValNSvV6jujIxKRVgnrZ/WpA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" + "@typescript-eslint/types": "8.0.0-alpha.13", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.13" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2213,26 +2231,23 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", - "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.13.tgz", + "integrity": "sha512-4Rkwf4YaQIqeF/l2/F9hqxiWlr9P5a0M7Ep0kK99WDYDAhABraIgvsScd2PAtKU1smJXdozbqKDywKCg6etH5Q==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", + "@typescript-eslint/utils": "8.0.0-alpha.13", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -2240,12 +2255,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.13.tgz", + "integrity": "sha512-uJN8BSa8YHs9E/lFR9nkzLyaEijXGzMLgkh8DuQMlOP8epqJ9Jnzx+gOFUIyJV+C8uavpGp+YnCMvq4IDG9L+Q==", "dev": true, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2253,13 +2268,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.13.tgz", + "integrity": "sha512-p5SZlikPhHg4510ttfqwjwMgBG7ehzRj5dLk8JR5x9bV0D8zWNotqkWNa0vftbYHb+mdGV6D9zNrJwDjWVd4ag==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "8.0.0-alpha.13", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2268,7 +2283,7 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2281,50 +2296,47 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.13.tgz", + "integrity": "sha512-BMAbK/fC7RgvRjGQh8AixfQErFXIpCqL1DfFmMnp+oBZvYLON/4/iIj0yPPpY3E8gbljT7s4LHVkNSu7K/MQug==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "8.0.0-alpha.13", + "@typescript-eslint/types": "8.0.0-alpha.13", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", + "semver": "^7.6.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.13.tgz", + "integrity": "sha512-q8r/RVc5AtAnnRGqKBIoWMwChET2hqydVGqAXlvXXnB6JOE3prpMLvnpUp7JK1jma3VPt2JieCGhzX3YZSLY/Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "8.0.0-alpha.13", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2462,15 +2474,15 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -2827,18 +2839,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2917,15 +2917,6 @@ "node": ">=0.2.0" } }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } - }, "node_modules/cacache": { "version": "18.0.3", "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", @@ -3187,9 +3178,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", - "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -3674,18 +3665,6 @@ "node": ">=6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -3944,41 +3923,37 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -3992,23 +3967,23 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4036,6 +4011,18 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4049,17 +4036,29 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4239,14 +4238,14 @@ "dev": true }, "node_modules/fast-json-stringify": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.13.0.tgz", - "integrity": "sha512-XjTDWKHP3GoMQUOfnjYUbqeHeEt+PvYgvBdG2fRSmYaORILbSr8xTJvZX+w1YSAP5pw2NwKrGRmQleYueZEoxw==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.15.1.tgz", + "integrity": "sha512-JopGtkvvguRqrS4gHXSSA2jf4pDgOZkeBAkLO1LbzOpiOMo7/kugoR+KiWifpLpluaVeYDkAuxCJOj4Gyc6L9A==", "dev": true, "dependencies": { "@fastify/merge-json-schemas": "^0.1.0", "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", + "ajv-formats": "^3.0.1", "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", "json-schema-ref-resolver": "^1.0.1", @@ -4254,21 +4253,38 @@ } }, "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -4376,15 +4392,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -4400,14 +4416,14 @@ } }, "node_modules/find-my-way": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz", - "integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.0.tgz", + "integrity": "sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" + "safe-regex2": "^3.1.0" }, "engines": { "node": ">=14" @@ -4430,74 +4446,16 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "node_modules/flatted": { @@ -4789,9 +4747,9 @@ } }, "node_modules/gaxios": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.4.0.tgz", - "integrity": "sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.6.0.tgz", + "integrity": "sha512-bpOZVQV5gthH/jVCSuYuokRo2bTKOcuBiVWpjmTn6C5Agl5zclGfTljuGsQZxwwDBkli+YhZhP4TdlqTnhOezQ==", "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -4864,22 +4822,22 @@ "dev": true }, "node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", + "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", "devOptional": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.6", "minimatch": "^9.0.1", "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "path-scurry": "^1.11.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4922,15 +4880,12 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4957,9 +4912,9 @@ } }, "node_modules/google-auth-library": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.7.0.tgz", - "integrity": "sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.10.0.tgz", + "integrity": "sha512-ol+oSa5NbcGdDqA+gZ3G3mev59OHBZksBTxY/tYwjtcp1H/scAFwJfSQU9/1RALoyZ7FslNbke8j4i3ipwlyuQ==", "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -4973,9 +4928,9 @@ } }, "node_modules/googleapis": { - "version": "134.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-134.0.0.tgz", - "integrity": "sha512-o8LhD1754W6MHWtpwAPeP1WUHgNxuMxCnLMDFlMKAA5kCMTNqX9/eaTXnkkAIv6YRfoKMQ6D1vyR6/biXuhE9g==", + "version": "137.1.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-137.1.0.tgz", + "integrity": "sha512-2L7SzN0FLHyQtFmyIxrcXhgust77067pkkduqkbIpDuj9JzVnByxsRrcRfUMFQam3rQkWW2B0f1i40IwKDWIVQ==", "dependencies": { "google-auth-library": "^9.0.0", "googleapis-common": "^7.0.0" @@ -4985,9 +4940,9 @@ } }, "node_modules/googleapis-common": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.1.0.tgz", - "integrity": "sha512-p3KHiWDBBWJEXk6SYauBEvxw5+UmRy7k2scxGtsNv9eHsTbpopJ3/7If4OrNnzJ9XMLg3IlyQXpVp8YPQsStiw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", "dependencies": { "extend": "^3.0.2", "gaxios": "^6.0.3", @@ -5204,17 +5159,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/hb-lib-tools/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/helmet": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", @@ -5371,6 +5315,21 @@ "node": "^18 || ^20" } }, + "node_modules/homebridge-config-ui-x/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/homebridge-lib": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.1.tgz", @@ -5390,18 +5349,6 @@ "node": "20.13.1||^20||^18" } }, - "node_modules/homebridge/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/hosted-git-info": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", @@ -5539,9 +5486,9 @@ "dev": true }, "node_modules/ignore-walk": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", - "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", "dev": true, "dependencies": { "minimatch": "^9.0.0" @@ -6155,9 +6102,9 @@ "dev": true }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", - "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -6349,9 +6296,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.59", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.59.tgz", - "integrity": "sha512-HeTsOrDF/hWhEiKqZVwg9Cqlep5x2T+IYDENvT2VRj3iX8JQ7Y+omENv+AIn0vC8m6GYhivfCed5Cgfw27r5SA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==", "dev": true }, "node_modules/light-my-request": { @@ -6662,9 +6609,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "devOptional": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -6683,9 +6630,9 @@ } }, "node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "devOptional": true, "dependencies": { "minipass": "^7.0.3", @@ -6943,9 +6890,9 @@ } }, "node_modules/node-abi": { - "version": "3.57.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", - "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -7254,9 +7201,9 @@ } }, "node_modules/npm-bundled": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", - "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", "dev": true, "dependencies": { "npm-normalize-package-bin": "^3.0.0" @@ -7655,9 +7602,9 @@ } }, "node_modules/npm-registry-fetch/node_modules/cacache/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -7898,17 +7845,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -8076,9 +8023,9 @@ } }, "node_modules/pacote/node_modules/cacache/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -8214,25 +8161,25 @@ } }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "devOptional": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "devOptional": true, "engines": { "node": "14 || >=16.14" @@ -8281,31 +8228,31 @@ } }, "node_modules/pino": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz", - "integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", + "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", - "thread-stream": "^2.0.0" + "thread-stream": "^2.6.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", "dev": true, "dependencies": { "readable-stream": "^4.0.0", @@ -8564,9 +8511,9 @@ } }, "node_modules/qs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", - "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dependencies": { "side-channel": "^1.0.6" }, @@ -8842,12 +8789,12 @@ "dev": true }, "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=10" } }, "node_modules/retry": { @@ -8943,12 +8890,12 @@ ] }, "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", "dev": true, "dependencies": { - "ret": "~0.2.0" + "ret": "~0.4.0" } }, "node_modules/safe-stable-stringify": { @@ -8979,13 +8926,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "devOptional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -9426,9 +9369,9 @@ } }, "node_modules/socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "devOptional": true, "dependencies": { "ip-address": "^9.0.5", @@ -9454,9 +9397,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", - "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0" @@ -9558,9 +9501,9 @@ "devOptional": true }, "node_modules/ssri": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", - "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "devOptional": true, "dependencies": { "minipass": "^7.0.3" @@ -9915,9 +9858,9 @@ } }, "node_modules/thread-stream": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", - "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", "dev": true, "dependencies": { "real-require": "^0.2.0" @@ -9965,32 +9908,14 @@ } }, "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, "bin": { "nodetouch": "bin/nodetouch.js" } }, - "node_modules/touch/node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -10254,12 +10179,12 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10311,9 +10236,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.16.1.tgz", - "integrity": "sha512-NeNiTT7ixpeiL1qOIU/xTVpHpVP0svmI6PwoCKaMGaI5AsHOaRdwqU/f7Fi9eyU4u03nd5U/BC8wmRMnS9nqoA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.17.0.tgz", + "integrity": "sha512-fs13QiDjPIzJ7gFAOal9CSG0c92rT2xw6MuMUJ4H30Eg5GCauLWYCCZA1tInjd6M4y+JZjVCCFr9pFpbhcC64w==", "engines": { "node": ">=18.17" } @@ -10556,21 +10481,18 @@ } }, "node_modules/validate-npm-package-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", - "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "dependencies": { - "builtins": "^5.0.0" - }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", "dev": true, "engines": { "node": ">= 0.10" @@ -10717,6 +10639,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 731dda3c..13c3df07 100644 --- a/package.json +++ b/package.json @@ -82,16 +82,16 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", - "undici": "^6.16.1" + "undici": "^6.17.0" }, "optionalDependencies": { "node-switchbot": "2.1.0" }, "devDependencies": { "@types/node": "^20.12.12", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", - "eslint": "^8.57.0", + "@typescript-eslint/eslint-plugin": "^8.0.0-alpha.13", + "@typescript-eslint/parser": "^8.0.0-alpha.13", + "eslint": "^9.3.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 02d8c518..ab2ef44d 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -436,9 +436,10 @@ export class BlindTilt extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/bot.ts b/src/device/bot.ts index 146224b4..1eedda12 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -519,9 +519,10 @@ export class Bot extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index f5ecf5b3..b17d147d 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -288,9 +288,10 @@ export class CeilingLight extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 3f34c9ca..12dd4b05 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -349,9 +349,10 @@ export class ColorBulb extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/contact.ts b/src/device/contact.ts index 35799afc..86ac899d 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -279,9 +279,10 @@ export class Contact extends deviceBase { + `StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 7ba11856..0953eb25 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -464,9 +464,10 @@ export class Curtain extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/device.ts b/src/device/device.ts index c28ccb14..7db7cd57 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -549,12 +549,13 @@ export abstract class deviceBase { device.version = this.platform.version; accessory.context.version = this.platform.version; } else { - accessory.context.version = device.version?.toString; + accessory.context.version = device.version?.replace(/^V|-.*$/g, ''); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${accessory.context.version}`); if (accessory.context.version) { this.accessory .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version) .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(this.accessory.context.version); diff --git a/src/device/fan.ts b/src/device/fan.ts index 7cb75c76..dced97c0 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -204,9 +204,10 @@ export class Fan extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/hub.ts b/src/device/hub.ts index 1c70f2d9..b1c9895f 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -217,9 +217,10 @@ export class Hub extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 2f49b063..0628b46a 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -268,9 +268,10 @@ export class Humidifier extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index b1e58d50..ddd6bdeb 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -228,9 +228,10 @@ export class IOSensor extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 682d6fef..e5d475e4 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -326,9 +326,10 @@ export class StripLight extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/lock.ts b/src/device/lock.ts index b4d0c65d..ff8babe8 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -256,9 +256,10 @@ export class Lock extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/meter.ts b/src/device/meter.ts index 3671c296..dcf327c5 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -224,9 +224,10 @@ export class Meter extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 563f34f0..4e1e6f5d 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -225,9 +225,10 @@ export class MeterPlus extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/motion.ts b/src/device/motion.ts index a181796c..4bb240df 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -200,9 +200,10 @@ export class Motion extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/plug.ts b/src/device/plug.ts index 9a55f5d8..82e53571 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -130,9 +130,10 @@ export class Plug extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet.On}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index d2a42042..841b989c 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -199,9 +199,10 @@ export class RobotVacuumCleaner extends deviceBase { + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index a271d87d..d41de61b 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -166,9 +166,10 @@ export class WaterDetector extends deviceBase { } // Firmware Version - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${deviceStatus.body.version}`); + const version = deviceStatus.body.version?.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = deviceStatus.body.version.toString(); + this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); this.accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) From b8d93c6318a4105263941f3f9e6f401fa8a54122 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 20:24:03 -0500 Subject: [PATCH 54/73] eslint --- eslint.config.js | 26 ++++--- package-lock.json | 105 ++++++++++++++++++++++++++-- package.json | 9 ++- src/device/fan.ts | 26 +++---- src/homebridge-ui/public/index.html | 2 +- src/homebridge-ui/server.ts | 2 +- src/irdevice/fan.ts | 2 - 7 files changed, 132 insertions(+), 40 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 7b9e3315..da699f30 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,19 +1,17 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + + export default [ + { languageOptions: { globals: globals.browser } }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, { - "parser": "@typescript-eslint/parser", - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" // uses the recommended rules from the @typescript-eslint/eslint-plugin - ], - "parserOptions": { - "ecmaVersion": 2021, - "sourceType": "module" - }, - "ignorePatterns": [ - "dist" - ], - "rules": { + ignores: [".dist/*"] + }, + { + rules: { "quotes": [ "warn", "single" diff --git a/package-lock.json b/package-lock.json index 358cdab4..33a64f7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,17 +27,20 @@ "undici": "^6.17.0" }, "devDependencies": { + "@eslint/js": "^9.3.0", + "@stylistic/eslint-plugin-js": "^2.1.0", + "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", - "@typescript-eslint/eslint-plugin": "^8.0.0-alpha.13", - "@typescript-eslint/parser": "^8.0.0-alpha.13", "eslint": "^9.3.0", + "globals": "^15.2.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", "rimraf": "^5.0.7", "ts-node": "^10.9.2", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "typescript-eslint": "^8.0.0-alpha.13" }, "engines": { "homebridge": "^1.8.2", @@ -176,6 +179,18 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2014,6 +2029,36 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, + "node_modules/@stylistic/eslint-plugin-js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz", + "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "acorn": "^8.11.3", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2096,6 +2141,31 @@ "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -4880,9 +4950,9 @@ } }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.2.0.tgz", + "integrity": "sha512-FQ5YwCHZM3nCmtb5FzEWwdUc9K5d3V/w9mzcz8iGD1gC/aOTHc6PouYu0kkKipNJqHAT7m51sqzQjEjIP+cK0A==", "dev": true, "engines": { "node": ">=18" @@ -10217,6 +10287,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.13.tgz", + "integrity": "sha512-DMFKGlZTYPGrjejz1G8Cp0LuvxlelQqy1BY/T95yNVK13aVYTnDrAZ5ytW9a79OzserqBDoJnXs1AFaUqJoNEg==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.13", + "@typescript-eslint/parser": "8.0.0-alpha.13", + "@typescript-eslint/utils": "8.0.0-alpha.13" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/uid": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", diff --git a/package.json b/package.json index 13c3df07..a2a94c47 100644 --- a/package.json +++ b/package.json @@ -88,16 +88,19 @@ "node-switchbot": "2.1.0" }, "devDependencies": { + "@eslint/js": "^9.3.0", + "@stylistic/eslint-plugin-js": "^2.1.0", + "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", - "@typescript-eslint/eslint-plugin": "^8.0.0-alpha.13", - "@typescript-eslint/parser": "^8.0.0-alpha.13", "eslint": "^9.3.0", + "globals": "^15.2.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", "nodemon": "^3.1.0", "npm-check-updates": "^16.14.20", "rimraf": "^5.0.7", "ts-node": "^10.9.2", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "typescript-eslint": "^8.0.0-alpha.13" } } diff --git a/src/device/fan.ts b/src/device/fan.ts index dced97c0..0c38ede2 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * plug.ts: @switchbot/homebridge-switchbot. @@ -121,11 +120,11 @@ export class Fan extends deviceBase { // Firmware Version if (version) { this.accessory.context.version = version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(this.accessory.context.version); } this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -303,12 +302,12 @@ export class Fan extends deviceBase { /** * Pushes the requested changes to the SwitchBot API - * deviceType commandType Command command parameter Description - * Battery Circulator Fan - "command" "turnOff" "default" = set to OFF state - * Battery Circulator Fan - "command" "turnOn" "default" = set to ON state - * Battery Circulator Fan - "command" "setNightLightMode" "off, 1, or 2" = off, turn off nightlight, (1, bright) (2, dim) - * Battery Circulator Fan - "command" "setWindMode" "direct, natural, sleep, or baby" = Set fan mode. direct: direct mode. natural: natural mode. sleep: sleep mode. baby: ultra quiet mode - * Battery Circulator Fan - "command" "setWindSpeed" "{1-100} e.g. 10" = Set fan speed.1~100 + * commandType command parameter Description + * "command" "turnOff" "default" = set to OFF state + * "command" "turnOn" "default" = set to ON state + * "command" "setNightLightMode" "off, 1, or 2" = off, turn off nightlight, (1, bright) (2, dim) + * "command" "setWindMode" "direct, natural, sleep, or baby" = Set fan mode + * "command" "setWindSpeed" "{1-100} e.g. 10" = Set fan speed 1~100 */ async pushChanges(): Promise { @@ -518,7 +517,8 @@ export class Fan extends deviceBase { } else { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.Battery.StatusLowBattery}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } } diff --git a/src/homebridge-ui/public/index.html b/src/homebridge-ui/public/index.html index e50467cb..bf5a8028 100644 --- a/src/homebridge-ui/public/index.html +++ b/src/homebridge-ui/public/index.html @@ -162,7 +162,7 @@
Help/About
document.getElementById('displayName').innerHTML = thisAcc.displayName; document.getElementById('deviceID').innerHTML = context.deviceID; document.getElementById('model').innerHTML = context.model; - document.getElementById('version').innerHTML = context.version || 'N/A'; + document.getElementById('version').innerHTML = context.version; document.getElementById('deviceType').innerHTML = context.deviceType; document.getElementById('connectionType').innerHTML = context.connectionType; document.getElementById('deviceTable').style.display = 'inline-table'; diff --git a/src/homebridge-ui/server.ts b/src/homebridge-ui/server.ts index 1cae72d4..65e33b82 100644 --- a/src/homebridge-ui/server.ts +++ b/src/homebridge-ui/server.ts @@ -31,7 +31,7 @@ class PluginUiServer extends HomebridgePluginUiServer { } // Return the array return devicesToReturn; - } catch (err) { + } catch { // Just return an empty accessory list in case of any errors return []; } diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index c2f1508a..4201f8e3 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -85,7 +85,6 @@ export class IRFan extends irdeviceBase { this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Rotation Speed Characteristic was removed.`); } else { - // eslint-disable-next-line max-len this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed Characteristic was not removed/added, ` + `Clear Cache on ${this.accessory.displayName} to remove Chracteristic`, @@ -100,7 +99,6 @@ export class IRFan extends irdeviceBase { this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was removed.`); } else { - // eslint-disable-next-line max-len this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was not removed/added, ` + `Clear Cache on ${this.accessory.displayName} To Remove Chracteristic`, From c2f61cae35e8d1e0d1908e2e8cae2661207c11ff Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 17 May 2024 21:13:35 -0500 Subject: [PATCH 55/73] less logs --- src/device/device.ts | 2 +- src/irdevice/irdevice.ts | 269 +++++++-------------------------------- 2 files changed, 45 insertions(+), 226 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index 7db7cd57..dc613eb0 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -279,7 +279,7 @@ export abstract class deviceBase { const config = Object.assign({}, deviceConfig, botConfig, curtainConfig, waterdetectorConfig, striplightConfig, plugConfig, iosensorConfig, meterConfig, humidifierConfig, hubConfig, lockConfig, ceilinglightConfig, colorbulbConfig, contactConfig, motionConfig, blindTiltConfig); if (Object.entries(config).length !== 0) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 00fe0aaf..943a6b5d 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -65,258 +65,77 @@ export abstract class irdeviceBase { } async getDeviceConfigSettings(device: irdevice & irDevicesConfig): Promise { - let config = {}; - if (device.logging !== undefined) { - config['logging'] = device.logging; + const deviceConfig = {}; + if (device.logging !== 'standard') { + deviceConfig['logging'] = device.logging; } - if (device.irair) { - config = device.irair; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; + if (device.connectionType !== '') { + deviceConfig['connectionType'] = device.connectionType; } - if (device.customize !== undefined) { - config['customize'] = device.customize; + if (device.external === true) { + deviceConfig['external'] = device.external; } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (device.disablePushDetail !== undefined) { - config['disablePushDetail'] = device.disablePushDetail; - } - if (device.irpur) { - config = device.irpur; + if (device.customize === true) { + deviceConfig['customize'] = device.customize; } - if (device.logging !== undefined) { - config['logging'] = device.logging; + if (device.commandType !== '') { + deviceConfig['commandType'] = device.commandType; } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; + if (device.customOn !== '') { + deviceConfig['customOn'] = device.customOn; } - if (device.external !== undefined) { - config['external'] = device.external; + if (device.customOff !== '') { + deviceConfig['customOff'] = device.customOff; } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; + if (device.disablePushOn === true) { + deviceConfig['disablePushOn'] = device.disablePushOn; } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; + if (device.disablePushOff === true) { + deviceConfig['disablePushOff'] = device.disablePushOff; } - if (device.customize !== undefined) { - config['customize'] = device.customize; + if (device.disablePushDetail === true) { + deviceConfig['disablePushDetail'] = device.disablePushDetail; } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; + let irairConfig = {}; + if (device.irair) { + irairConfig = device.irair; } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + let irpurConfig = {}; + if (device.irpur) { + irpurConfig = device.irpur; } + let ircamConfig = {}; if (device.ircam) { - config = device.ircam; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + ircamConfig = device.ircam; } + let irfanConfig = {}; if (device.irfan) { - config = device.irfan; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + irfanConfig = device.irfan; } + let irlightConfig = {}; if (device.irlight) { - config = device.irlight; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + irlightConfig = device.irlight; } + let otherConfig = {}; if (device.other) { - config = device.other; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + otherConfig = device.other; } + let irtvConfig = {}; if (device.irtv) { - config = device.irtv; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; - } - if (device.disablePushDetail !== undefined) { - config['disablePushDetail'] = device.disablePushDetail; + irtvConfig = device.irtv; } + let irvcConfig = {}; if (device.irvc) { - config = device.irvc; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + irvcConfig = device.irvc; } + let irwhConfig = {}; if (device.irwh) { - config = device.irwh; - } - if (device.logging !== undefined) { - config['logging'] = device.logging; - } - if (device.connectionType !== undefined) { - config['connectionType'] = device.connectionType; - } - if (device.external !== undefined) { - config['external'] = device.external; - } - if (device.customOn !== undefined) { - config['customOn'] = device.customOn; - } - if (device.customOff !== undefined) { - config['customOff'] = device.customOff; - } - if (device.customize !== undefined) { - config['customize'] = device.customize; - } - if (device.disablePushOn !== undefined) { - config['disablePushOn'] = device.disablePushOn; - } - if (device.disablePushOff !== undefined) { - config['disablePushOff'] = device.disablePushOff; + irwhConfig = device.irwh; } + const config = Object.assign({}, deviceConfig, irairConfig, irpurConfig, ircamConfig, irfanConfig, irlightConfig, otherConfig, + irtvConfig, irvcConfig, irwhConfig); if (Object.entries(config).length !== 0) { - this.infoLog(`Lock: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); } } From f7e24b204d2685444745f1c2d2fe640edb2bba80 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sat, 18 May 2024 11:10:27 -0500 Subject: [PATCH 56/73] Update package-lock.json --- package-lock.json | 408 ++++++++++++++++++---------------------------- 1 file changed, 158 insertions(+), 250 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33a64f7b..a6ca34ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -137,6 +137,18 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", @@ -169,16 +181,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -191,18 +193,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", @@ -482,28 +472,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -660,16 +628,6 @@ "node": ">= 6.0.0" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@mapbox/node-pre-gyp/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -703,18 +661,6 @@ "node": ">= 6" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1110,16 +1056,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@npmcli/move-file/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@npmcli/move-file/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1140,18 +1076,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/move-file/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@npmcli/move-file/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -1270,13 +1194,12 @@ } }, "node_modules/@npmcli/run-script/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/@npmcli/run-script/node_modules/cacache": { @@ -1308,15 +1231,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@npmcli/run-script/node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@npmcli/run-script/node_modules/cacache/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -1468,18 +1382,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@npmcli/run-script/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@npmcli/run-script/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", @@ -2047,18 +1949,6 @@ "eslint": ">=8.40.0" } }, - "node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2126,6 +2016,30 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -2365,6 +2279,30 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "8.0.0-alpha.13", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.13.tgz", @@ -2407,6 +2345,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2910,12 +2860,12 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "devOptional": true, + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -4060,28 +4010,6 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", @@ -4093,18 +4021,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", @@ -4122,18 +4038,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -4691,16 +4595,6 @@ "node": ">=0.6" } }, - "node_modules/fstream/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/fstream/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4721,18 +4615,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fstream/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/fstream/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -4925,6 +4807,30 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "devOptional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "devOptional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -5247,15 +5153,6 @@ "readable-stream": "^3.6.0" } }, - "node_modules/help-me/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/help-me/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5275,17 +5172,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/help-me/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/hexy": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.3.5.tgz", @@ -5567,6 +5453,30 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6656,18 +6566,14 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "devOptional": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { @@ -7155,16 +7061,6 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -7174,18 +7070,6 @@ "node": ">=4" } }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7354,6 +7238,15 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/npm-check-updates/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/npm-check-updates/node_modules/cacache": { "version": "17.1.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", @@ -7469,6 +7362,21 @@ "node": ">=8" } }, + "node_modules/npm-check-updates/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/npm-check-updates/node_modules/minipass-collect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", From 5838f740b9b2a4b4b333fb9c051c40a889e71c3d Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sat, 18 May 2024 11:13:52 -0500 Subject: [PATCH 57/73] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f6dfaa..a415d568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) -## [BETA-3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-04-XX) +## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-05-XX) ### What's Changed - Add Support for `Water Detector` @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. This projec - Add BLE support for `Smart Lock` - Add `K10+` deviceType Support - Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev) +- Major Refactoring of `device` and `irdevice` files. - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 From 270bdab2a2244b15212d83dc1a351a1d10ce37e7 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sat, 18 May 2024 11:52:58 -0500 Subject: [PATCH 58/73] fix linting --- eslint.config.js | 110 +++++++++++++++++++++++----------------------- package-lock.json | 8 ++-- package.json | 2 +- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index da699f30..e322db0c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,6 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; +import globals from 'globals'; +import pluginJs from '@eslint/js'; +import tseslint from 'typescript-eslint'; export default [ @@ -8,78 +8,78 @@ export default [ pluginJs.configs.recommended, ...tseslint.configs.recommended, { - ignores: [".dist/*"] + ignores: ['.dist/*'], }, { rules: { - "quotes": [ - "warn", - "single" + 'quotes': [ + 'warn', + 'single', ], - "indent": [ - "warn", + 'indent': [ + 'warn', 2, { - "SwitchCase": 1 - } + 'SwitchCase': 1, + }, ], - "linebreak-style": [ - "warn", - "unix" + 'linebreak-style': [ + 'warn', + 'unix', ], - "semi": [ - "warn", - "always" + 'semi': [ + 'warn', + 'always', ], - "comma-dangle": [ - "warn", - "always-multiline" + 'comma-dangle': [ + 'warn', + 'always-multiline', ], - "dot-notation": "off", - "eqeqeq": "warn", - "curly": [ - "warn", - "all" + 'dot-notation': 'off', + 'eqeqeq': 'warn', + 'curly': [ + 'warn', + 'all', ], - "brace-style": [ - "warn" + 'brace-style': [ + 'warn', ], - "prefer-arrow-callback": [ - "warn" + 'prefer-arrow-callback': [ + 'warn', ], - "max-len": [ - "warn", - 150 + 'max-len': [ + 'warn', + 150, ], - "no-console": [ - "warn" + 'no-console': [ + 'warn', ], // use the provided Homebridge log method instead - "no-non-null-assertion": [ - "off" + 'no-non-null-assertion': [ + 'off', ], - "comma-spacing": [ - "error" + 'comma-spacing': [ + 'error', ], - "no-multi-spaces": [ - "warn", + 'no-multi-spaces': [ + 'warn', { - "ignoreEOLComments": true - } + 'ignoreEOLComments': true, + }, ], - "no-trailing-spaces": [ - "warn" + 'no-trailing-spaces': [ + 'warn', ], - "lines-between-class-members": [ - "warn", - "always", + 'lines-between-class-members': [ + 'warn', + 'always', { - "exceptAfterSingleLine": true - } + 'exceptAfterSingleLine': true, + }, ], - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-explicit-any": "off" - } - } + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, ]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a6ca34ae..c78d4bac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "node": "^18 || ^20 || ^22" }, "optionalDependencies": { - "node-switchbot": "2.1.0" + "node-switchbot": "2.1.1-beta.0" } }, "node_modules/@abandonware/bluetooth-hci-socket": { @@ -7022,9 +7022,9 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.0.tgz", - "integrity": "sha512-KHjZJTLQrgqaUqjqOf/1C4docddRslBLSFkqvgHzzzvsyJX0NDu+y2J9/Bs2miZvUwGjGGJNRx//5ertHstjEA==", + "version": "2.1.1-beta.0", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.1-beta.0.tgz", + "integrity": "sha512-dfGWEwvxCgz+7aeqR7cr8/WOCKxE1W5MHplkXsIBK7XfSSnnWVzX1ct7Pu4JUKh/RCAnD9BqiMtdXUKr6/LvxA==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-25" diff --git a/package.json b/package.json index a2a94c47..7f58e2ea 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "undici": "^6.17.0" }, "optionalDependencies": { - "node-switchbot": "2.1.0" + "node-switchbot": "2.1.1-beta.0" }, "devDependencies": { "@eslint/js": "^9.3.0", From e487dd804d6029ae2ecd0294855624417a73ad22 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sat, 18 May 2024 16:38:33 -0500 Subject: [PATCH 59/73] fix for #971 --- src/device/blindtilt.ts | 6 +++--- src/device/bot.ts | 22 +++++++++++----------- src/device/ceilinglight.ts | 2 +- src/device/colorbulb.ts | 2 +- src/device/contact.ts | 6 +++--- src/device/curtain.ts | 6 +++--- src/device/fan.ts | 4 ++-- src/device/hub.ts | 6 +++--- src/device/humidifier.ts | 4 ++-- src/device/iosensor.ts | 6 +++--- src/device/lightstrip.ts | 2 +- src/device/lock.ts | 8 ++++---- src/device/meter.ts | 6 +++--- src/device/meterplus.ts | 6 +++--- src/device/motion.ts | 21 ++++++++++++++++++--- src/device/plug.ts | 2 +- src/device/robotvacuumcleaner.ts | 4 ++-- src/device/waterdetector.ts | 4 ++-- src/irdevice/airconditioner.ts | 4 ++-- src/irdevice/airpurifier.ts | 4 ++-- src/irdevice/camera.ts | 2 +- src/irdevice/fan.ts | 2 +- src/irdevice/light.ts | 2 +- src/irdevice/other.ts | 20 ++++++++++---------- src/irdevice/tv.ts | 4 ++-- src/irdevice/vacuumcleaner.ts | 2 +- src/irdevice/waterheater.ts | 2 +- 27 files changed, 87 insertions(+), 72 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index ab2ef44d..58bf9e1f 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -62,7 +62,7 @@ export class BlindTilt extends deviceBase { // Initialize LightBulb property this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering)!, + Service: accessory.getService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition || 100, CurrentPosition: accessory.context.CurrentPosition || 100, @@ -72,7 +72,7 @@ export class BlindTilt extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -80,7 +80,7 @@ export class BlindTilt extends deviceBase { // Initialize LightSensor property if (!this.device.blindTilt?.hide_lightsensor) { this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor)!, + Service: accessory.getService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } diff --git a/src/device/bot.ts b/src/device/bot.ts index 1eedda12..60e475df 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -100,7 +100,7 @@ export class Bot extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -109,7 +109,7 @@ export class Bot extends deviceBase { if (this.botDeviceType === 'switch') { // Initialize Switch property this.Switch = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -133,7 +133,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'garagedoor') { // Initialize Switch property this.GarageDoor = { - Service: accessory.getService(this.hap.Service.GarageDoorOpener)!, + Service: accessory.getService(this.hap.Service.GarageDoorOpener) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -158,7 +158,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'door') { // Initialize Switch property this.Door = { - Service: accessory.getService(this.hap.Service.Door)!, + Service: accessory.getService(this.hap.Service.Door) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -191,7 +191,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'window') { // Initialize Switch property this.Window = { - Service: accessory.getService(this.hap.Service.Window)!, + Service: accessory.getService(this.hap.Service.Window) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -224,7 +224,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'windowcovering') { // Initialize Switch property this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering)!, + Service: accessory.getService(this.hap.Service.WindowCovering) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -257,7 +257,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'lock') { // Initialize Switch property this.Lock = { - Service: accessory.getService(this.hap.Service.LockMechanism)!, + Service: accessory.getService(this.hap.Service.LockMechanism) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -281,7 +281,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'faucet') { // Initialize Switch property this.Faucet = { - Service: accessory.getService(this.hap.Service.Faucet)!, + Service: accessory.getService(this.hap.Service.Faucet) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -305,7 +305,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'fan') { // Initialize Switch property this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2)!, + Service: accessory.getService(this.hap.Service.Fanv2) as Service, On: accessory.context.On || false, }; this.removeLockService(accessory); @@ -329,7 +329,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'stateful') { // Initialize Switch property this.StatefulProgrammableSwitch = { - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch)!, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -355,7 +355,7 @@ export class Bot extends deviceBase { } else { // Initialize Switch property this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet)!, + Service: accessory.getService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index b17d147d..1fc2c349 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -50,7 +50,7 @@ export class CeilingLight extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 12dd4b05..511c1242 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -51,7 +51,7 @@ export class ColorBulb extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, diff --git a/src/device/contact.ts b/src/device/contact.ts index 86ac899d..55d2ac36 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -53,14 +53,14 @@ export class Contact extends deviceBase { // Initialize Contact Sensor property this.ContactSensor = { - Service: accessory.getService(this.hap.Service.ContactSensor)!, + Service: accessory.getService(this.hap.Service.ContactSensor) as Service, ContactSensorState: this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; // Initialize Motion Sensor property if (!this.device.contact?.hide_motionsensor) { this.MotionSensor = { - Service: accessory.getService(this.hap.Service.MotionSensor)!, + Service: accessory.getService(this.hap.Service.MotionSensor) as Service, MotionDetected: false, }; } @@ -68,7 +68,7 @@ export class Contact extends deviceBase { // Initialize Light Sensor property if (!this.device.contact?.hide_lightsensor) { this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor)!, + Service: accessory.getService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: 0, }; } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 0953eb25..fa90d757 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -55,7 +55,7 @@ export class Curtain extends deviceBase { // Initialize LightBulb property this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering)!, + Service: accessory.getService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition || 100, CurrentPosition: accessory.context.CurrentPosition || 100, @@ -64,7 +64,7 @@ export class Curtain extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -72,7 +72,7 @@ export class Curtain extends deviceBase { // Initialize LightSensor property if (!this.device.curtain?.hide_lightsensor) { this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor)!, + Service: accessory.getService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } diff --git a/src/device/fan.ts b/src/device/fan.ts index 0c38ede2..b4bc30f7 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -41,7 +41,7 @@ export class Fan extends deviceBase { // Initialize Fan property this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2)!, + Service: accessory.getService(this.hap.Service.Fanv2) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, RotationSpeed: accessory.context.RotationSpeed || 0, @@ -49,7 +49,7 @@ export class Fan extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGING, diff --git a/src/device/hub.ts b/src/device/hub.ts index b1c9895f..551d5a47 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -43,7 +43,7 @@ export class Hub extends deviceBase { // Initialize Temperature Sensor property if (!device.hub?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 0, }; } @@ -51,7 +51,7 @@ export class Hub extends deviceBase { // Initialize Humidity Sensor property if (!device.hub?.hide_humidity) { this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor)!, + Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 0, }; } @@ -59,7 +59,7 @@ export class Hub extends deviceBase { // Initialize Light Sensor property if (!device.hub?.hide_lightsensor) { this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor)!, + Service: accessory.getService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, }; } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 0628b46a..ac0d1226 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -45,7 +45,7 @@ export class Humidifier extends deviceBase { // Initialize the HumidifierDehumidifier Service this.HumidifierDehumidifier = { - Service: accessory.getService(this.hap.Service.HumidifierDehumidifier)!, + Service: accessory.getService(this.hap.Service.HumidifierDehumidifier) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.ACTIVE, WaterLevel: accessory.context.WaterLevel || 100, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, @@ -59,7 +59,7 @@ export class Humidifier extends deviceBase { // Initialize the Temperature Sensor Service if (!device.humidifier?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index ddd6bdeb..c764d3bf 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -49,7 +49,7 @@ export class IOSensor extends deviceBase { // Initialize Temperature Sensor property if (!device.iosensor?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -57,14 +57,14 @@ export class IOSensor extends deviceBase { // Initialize Humidity Sensor property if (!device.iosensor?.hide_humidity) { this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor)!, + Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index e5d475e4..6e221343 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -44,7 +44,7 @@ export class StripLight extends deviceBase { // Initialize the LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, Hue: accessory.context.Hue || 0, Saturation: accessory.context.Saturation || 0, diff --git a/src/device/lock.ts b/src/device/lock.ts index ff8babe8..3d19b2a3 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -46,14 +46,14 @@ export class Lock extends deviceBase { // Initialize LockMechanism property this.LockMechanism = { - Service: accessory.getService(this.hap.Service.LockMechanism)!, + Service: accessory.getService(this.hap.Service.LockMechanism) as Service, LockTargetState: accessory.context.LockTargetState || this.hap.Characteristic.LockTargetState.SECURED, LockCurrentState: accessory.context.LockCurrentState || this.hap.Characteristic.LockCurrentState.SECURED, }; // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -61,7 +61,7 @@ export class Lock extends deviceBase { // Initialize ContactSensor property if (!this.device.lock?.hide_contactsensor) { this.ContactSensor = { - Service: accessory.getService(this.hap.Service.ContactSensor)!, + Service: accessory.getService(this.hap.Service.ContactSensor) as Service, ContactSensorState: accessory.context.ContactSensorState || this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; } @@ -69,7 +69,7 @@ export class Lock extends deviceBase { // Initialize Latch Button Service if (device.lock?.activate_latchbutton) { this.Switch = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, On: accessory.context.On || false, }; } diff --git a/src/device/meter.ts b/src/device/meter.ts index dcf327c5..c55fa4d3 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -44,7 +44,7 @@ export class Meter extends deviceBase { // Initialize Temperature Sensor property if (!device.meter?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -52,14 +52,14 @@ export class Meter extends deviceBase { // Initialize Humidity Sensor property if (!device.meter?.hide_humidity) { this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor)!, + Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 4e1e6f5d..78262239 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -48,7 +48,7 @@ export class MeterPlus extends deviceBase { // Initialize Temperature Sensor property if (!device.meter?.hide_temperature) { this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; } @@ -56,14 +56,14 @@ export class MeterPlus extends deviceBase { // Initialize Humidity Sensor property if (!device.meter?.hide_humidity) { this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor)!, + Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, }; } // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; diff --git a/src/device/motion.ts b/src/device/motion.ts index 4bb240df..8c1df48f 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -48,18 +48,33 @@ export class Motion extends deviceBase { // Initialize Motion Sensor property this.MotionSensor = { - Service: accessory.getService(this.hap.Service.MotionSensor)!, + Service: accessory.getService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected || false, }; + // Initialize Battery property + this.Battery = { + Service: accessory.getService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel || 100, + StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; + + // Initialize Motion Sensor property + if (!device.motion?.hide_lightsensor) { + this.LightSensor = { + Service: accessory.getService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0, + }; + }; + + // Retrieve initial values and updateHomekit this.refreshStatus(); // get the Battery service if it exists, otherwise create a new Motion service // you can create multiple services for each accessory - const MotionSensorService = `${accessory.displayName} Motion Sensor`; (this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) - || accessory.addService(this.hap.Service.MotionSensor)), MotionSensorService; + || accessory.addService(this.hap.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`; this.MotionSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type diff --git a/src/device/plug.ts b/src/device/plug.ts index 82e53571..75812b33 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -31,7 +31,7 @@ export class Plug extends deviceBase { // Initialize Outlet property this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet)!, + Service: accessory.getService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 841b989c..79c2ec5a 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -40,14 +40,14 @@ export class RobotVacuumCleaner extends deviceBase { // Initialize Lightbulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, Brightness: accessory.context.Brightness || 0, }; // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index d41de61b..e08d77ec 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -48,7 +48,7 @@ export class WaterDetector extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery)!, + Service: accessory.getService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, @@ -57,7 +57,7 @@ export class WaterDetector extends deviceBase { // Initialize Leak Sensor property if (!this.device.waterdetector?.hide_leak) { this.LeakSensor = { - Service: accessory.getService(this.hap.Service.LeakSensor)!, + Service: accessory.getService(this.hap.Service.LeakSensor) as Service, StatusActive: accessory.context.StatusActive || false, LeakDetected: accessory.context.LeakDetected || this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, }; diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index 6ac665e5..b8ea9ed4 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -58,7 +58,7 @@ export class AirConditioner extends irdeviceBase { // Initialize HeaterCooler property this.HeaterCooler = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState || this.hap.Characteristic.CurrentHeaterCoolerState.IDLE, TargetHeaterCoolerState: accessory.context.TargetHeaterCoolerState || this.hap.Characteristic.TargetHeaterCoolerState.AUTO, @@ -72,7 +72,7 @@ export class AirConditioner extends irdeviceBase { const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); this.HumiditySensor = { - Service: this.meter!.getService(this.hap.Service.HumiditySensor)!, + Service: this.meter!.getService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, }; } diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 261278f7..b7742167 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -54,7 +54,7 @@ export class AirPurifier extends irdeviceBase { // Initialize AirPurifier property this.AirPurifier = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, RotationSpeed: accessory.context.RotationSpeed || 0, CurrentAirPurifierState: accessory.context.CurrentAirPurifierState || this.hap.Characteristic.CurrentAirPurifierState.INACTIVE, @@ -63,7 +63,7 @@ export class AirPurifier extends irdeviceBase { // Initialize TemperatureSensor property this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor)!, + Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 24, }; diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 6b3ecd6b..bf718eac 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -29,7 +29,7 @@ export class Camera extends irdeviceBase { // Initialize Switch property this.Switch = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, On: accessory.context.On || false, }; diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 4201f8e3..4fffcc97 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -37,7 +37,7 @@ export class IRFan extends irdeviceBase { // Initialize Switch property this.Fan = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, RotationSpeed: accessory.context.RotationSpeed || 0, diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 2ad2194d..bd3fbfdc 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -40,7 +40,7 @@ export class Light extends irdeviceBase { if (!device.irlight?.stateless) { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)!, + Service: accessory.getService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, }; // get the Light service if it exists, otherwise create a new Light service diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 7bdb391f..095bec5d 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -82,7 +82,7 @@ export class Others extends irdeviceBase { if (this.otherDeviceType === 'switch') { // Initialize Switch property this.Switch = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -106,7 +106,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'garagedoor') { // Initialize Garage Door property this.GarageDoor = { - Service: accessory.getService(this.hap.Service.GarageDoorOpener)!, + Service: accessory.getService(this.hap.Service.GarageDoorOpener) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -131,7 +131,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'door') { // Initialize Door property this.Door = { - Service: accessory.getService(this.hap.Service.Door)!, + Service: accessory.getService(this.hap.Service.Door) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -164,7 +164,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'window') { // Initialize Window property this.Window = { - Service: accessory.getService(this.hap.Service.Window)!, + Service: accessory.getService(this.hap.Service.Window) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -197,7 +197,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'windowcovering') { // Initialize WindowCovering property this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering)!, + Service: accessory.getService(this.hap.Service.WindowCovering) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -230,7 +230,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'lock') { // Initialize Lock property this.Lock = { - Service: accessory.getService(this.hap.Service.LockMechanism)!, + Service: accessory.getService(this.hap.Service.LockMechanism) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -254,7 +254,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'faucet') { // Initialize Faucet property this.Faucet = { - Service: accessory.getService(this.hap.Service.Faucet)!, + Service: accessory.getService(this.hap.Service.Faucet) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -278,7 +278,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'fan') { // Initialize Fan property this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2)!, + Service: accessory.getService(this.hap.Service.Fanv2) as Service, On: accessory.context.On || false, }; this.removeLockService(accessory); @@ -302,7 +302,7 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'stateful') { // Initialize StatefulProgrammableSwitch property this.StatefulProgrammableSwitch = { - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch)!, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); @@ -328,7 +328,7 @@ export class Others extends irdeviceBase { } else { // Initialize Outlet property this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet)!, + Service: accessory.getService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; this.removeFanService(accessory); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index cf76edb4..9e47838b 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -41,7 +41,7 @@ export class TV extends irdeviceBase { // Initialize Television property this.Television = { - Service: accessory.getService(this.hap.Service.Television)!, + Service: accessory.getService(this.hap.Service.Television) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, ActiveIdentifier: accessory.context.ActiveIdentifier || 1, SleepDiscoveryMode: accessory.context.SleepDiscoveryMode || this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, @@ -50,7 +50,7 @@ export class TV extends irdeviceBase { // Initialize TelevisionSpeaker property this.TelevisionSpeaker = { - Service: accessory.getService(this.hap.Service.TelevisionSpeaker)!, + Service: accessory.getService(this.hap.Service.TelevisionSpeaker) as Service, Active: accessory.context.Active || false, VolumeControlType: accessory.context.VolumeControlType || this.hap.Characteristic.VolumeControlType.ABSOLUTE, VolumeSelector: accessory.context.VolumeSelector || this.hap.Characteristic.VolumeSelector.INCREMENT, diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 7b98ccc8..954b0185 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -29,7 +29,7 @@ export class VacuumCleaner extends irdeviceBase { // Initialize Switch property this.Switch = { - Service: accessory.getService(this.hap.Service.Switch)!, + Service: accessory.getService(this.hap.Service.Switch) as Service, On: accessory.context.On || false, }; diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index ef73d444..c4b4b551 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -29,7 +29,7 @@ export class WaterHeater extends irdeviceBase { // Initialize Valve property this.Valve = { - Service: accessory.getService(this.hap.Service.Valve)!, + Service: accessory.getService(this.hap.Service.Valve) as Service, Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, }; From 8ef9af819124d2301ec64ec1ef702bdfc0094319 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sat, 18 May 2024 23:22:11 -0500 Subject: [PATCH 60/73] more linting --- .vscode/settings.json | 5 +- eslint.config.js | 167 +++++----- package-lock.json | 456 ++++++++++++++++++++++++---- package.json | 4 +- src/device/blindtilt.ts | 10 +- src/device/bot.ts | 10 +- src/device/ceilinglight.ts | 10 +- src/device/colorbulb.ts | 8 +- src/device/contact.ts | 29 +- src/device/curtain.ts | 12 +- src/device/device.ts | 9 +- src/device/fan.ts | 8 +- src/device/hub.ts | 11 +- src/device/humidifier.ts | 12 +- src/device/iosensor.ts | 13 +- src/device/lightstrip.ts | 10 +- src/device/lock.ts | 8 +- src/device/meter.ts | 11 +- src/device/meterplus.ts | 11 +- src/device/motion.ts | 37 ++- src/device/plug.ts | 8 +- src/device/robotvacuumcleaner.ts | 12 +- src/device/waterdetector.ts | 8 +- src/homebridge-ui/public/index.html | 34 ++- src/index.ts | 5 +- src/irdevice/airconditioner.ts | 24 +- src/irdevice/airpurifier.ts | 8 +- src/irdevice/camera.ts | 10 +- src/irdevice/fan.ts | 71 +++-- src/irdevice/irdevice.ts | 9 +- src/irdevice/light.ts | 186 +++++------- src/irdevice/other.ts | 8 +- src/irdevice/tv.ts | 12 +- src/irdevice/vacuumcleaner.ts | 10 +- src/irdevice/waterheater.ts | 10 +- src/platform.ts | 125 ++++---- src/settings.ts | 131 ++++---- 37 files changed, 966 insertions(+), 546 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d1d60974..848d5c50 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -31,5 +31,8 @@ "editor.defaultFormatter": "vscode.json-language-features" }, "codeQL.githubDatabase.update": "never", - "codeQL.githubDatabase.download": "never" + "codeQL.githubDatabase.download": "never", + "[html]": { + "editor.defaultFormatter": "vscode.html-language-features" + } } \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index e322db0c..6890c8c0 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,85 +1,96 @@ -import globals from 'globals'; import pluginJs from '@eslint/js'; import tseslint from 'typescript-eslint'; +import stylistic from '@stylistic/eslint-plugin' -export default [ - { languageOptions: { globals: globals.browser } }, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - { - ignores: ['.dist/*'], +export default tseslint.config({ + plugins: { + '@stylistic': stylistic, + '@typescript-eslint': tseslint.plugin, }, - { - rules: { - 'quotes': [ - 'warn', - 'single', - ], - 'indent': [ - 'warn', - 2, - { - 'SwitchCase': 1, - }, - ], - 'linebreak-style': [ - 'warn', - 'unix', - ], - 'semi': [ - 'warn', - 'always', - ], - 'comma-dangle': [ - 'warn', - 'always-multiline', - ], - 'dot-notation': 'off', - 'eqeqeq': 'warn', - 'curly': [ - 'warn', - 'all', - ], - 'brace-style': [ - 'warn', - ], - 'prefer-arrow-callback': [ - 'warn', - ], - 'max-len': [ - 'warn', - 150, - ], - 'no-console': [ - 'warn', - ], // use the provided Homebridge log method instead - 'no-non-null-assertion': [ - 'off', - ], - 'comma-spacing': [ - 'error', - ], - 'no-multi-spaces': [ - 'warn', - { - 'ignoreEOLComments': true, - }, - ], - 'no-trailing-spaces': [ - 'warn', - ], - 'lines-between-class-members': [ - 'warn', - 'always', - { - 'exceptAfterSingleLine': true, - }, - ], - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: true, }, }, -]; \ No newline at end of file + files: ['**/*.ts'], + ignores: ['.dist/*'], + extends: [ + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + ], + rules: { + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + '@stylistic/type-annotation-spacing': 'error', + '@stylistic/quotes': [ + 'warn', + 'single', + ], + '@stylistic/indent': [ + 'warn', + 2, + { + 'SwitchCase': 1, + }, + ], + '@stylistic/linebreak-style': [ + 'warn', + 'unix', + ], + '@stylistic/semi': [ + 'warn', + 'always', + ], + '@stylistic/comma-dangle': [ + 'warn', + 'always-multiline', + ], + '@stylistic/dot-notation': 'off', + 'eqeqeq': 'warn', + 'curly': [ + 'warn', + 'all', + ], + '@stylistic/brace-style': [ + 'warn', + ], + 'prefer-arrow-callback': [ + 'warn', + ], + '@stylistic/max-len': [ + 'warn', + 150, + ], + 'no-console': [ + 'warn', + ], // use the provided Homebridge log method instead + 'no-non-null-assertion': [ + 'off', + ], + '@stylistic/comma-spacing': [ + 'error', + ], + '@stylistic/no-multi-spaces': [ + 'warn', + { + 'ignoreEOLComments': true, + }, + ], + '@stylistic/no-trailing-spaces': [ + 'warn', + ], + '@stylistic/lines-between-class-members': [ + 'warn', + 'always', + { + 'exceptAfterSingleLine': true, + }, + ], + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c78d4bac..b967dd3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ }, "devDependencies": { "@eslint/js": "^9.3.0", - "@stylistic/eslint-plugin-js": "^2.1.0", + "@stylistic/eslint-plugin": "^2.1.0", "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", "eslint": "^9.3.0", @@ -40,7 +40,7 @@ "rimraf": "^5.0.7", "ts-node": "^10.9.2", "typescript": "^5.4.5", - "typescript-eslint": "^8.0.0-alpha.13" + "typescript-eslint": "^8.0.0-alpha.14" }, "engines": { "homebridge": "^1.8.2", @@ -1931,6 +1931,25 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.1.0.tgz", + "integrity": "sha512-cBBowKP2u/+uE5CzgH5w8pE9VKqcM7BXdIDPIbGt2rmLJGnA6MJPr9vYGaqgMoJFs7R/FzsMQerMvvEP40g2uw==", + "dev": true, + "dependencies": { + "@stylistic/eslint-plugin-js": "2.1.0", + "@stylistic/eslint-plugin-jsx": "2.1.0", + "@stylistic/eslint-plugin-plus": "2.1.0", + "@stylistic/eslint-plugin-ts": "2.1.0", + "@types/eslint": "^8.56.10" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, "node_modules/@stylistic/eslint-plugin-js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz", @@ -1949,6 +1968,332 @@ "eslint": ">=8.40.0" } }, + "node_modules/@stylistic/eslint-plugin-jsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.1.0.tgz", + "integrity": "sha512-mMD7S+IndZo2vxmwpHVTCwx2O1VdtE5tmpeNwgaEcXODzWV1WTWpnsc/PECQKIr/mkLPFWiSIqcuYNhQ/3l6AQ==", + "dev": true, + "dependencies": { + "@stylistic/eslint-plugin-js": "^2.1.0", + "@types/eslint": "^8.56.10", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin-jsx/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@stylistic/eslint-plugin-plus": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz", + "integrity": "sha512-S5QAlgYXESJaSBFhBSBLZy9o36gXrXQwWSt6QkO+F0SrT9vpV5JF/VKoh+ojO7tHzd8Ckmyouq02TT9Sv2B0zQ==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-plus/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@stylistic/eslint-plugin-ts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.1.0.tgz", + "integrity": "sha512-2ioFibufHYBALx2TBrU4KXovCkN8qCqcb9yIHc0fyOfTaO5jw4d56WW7YRcF3Zgde6qFyXwAN6z/+w4pnmos1g==", + "dev": true, + "dependencies": { + "@stylistic/eslint-plugin-js": "2.1.0", + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin-ts/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2110,12 +2455,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, "node_modules/@types/semver-utils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", @@ -2135,21 +2474,19 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.13.tgz", - "integrity": "sha512-FQeu2HGVZ6wDAn2m6MxpS+or7LyBEjjCXnFv79aCJtcJnxtgDQa0po88GHZv1tZwzIsgxQ3bnbz4vNgbPisMbA==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.14.tgz", + "integrity": "sha512-tfw3zfCg+ynwARhVsuMXKBrmWCtqQ2Cr/cjPAuyKhJGY8t069Lc0Y+F5H7oDLlmm+G54v8lAHkTkw4K/p+PpFQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.13", - "@typescript-eslint/type-utils": "8.0.0-alpha.13", - "@typescript-eslint/utils": "8.0.0-alpha.13", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "8.0.0-alpha.14", + "@typescript-eslint/type-utils": "8.0.0-alpha.14", + "@typescript-eslint/utils": "8.0.0-alpha.14", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -2170,15 +2507,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.13.tgz", - "integrity": "sha512-CWFhVzrA6n7OeTHmvtxxUy+BSvUwHl8BcT6QsVi87MGcGQpLQZ1TeU0kYoIISkzFojoULF5q0de9hP/FbANi/g==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.14.tgz", + "integrity": "sha512-fD+DFo6aJJYyX4w712HzmE7QmUkoUvtlsFO/MqmYMeHIe0Pz5JZpJ1aYVbdxctazOb7NoW3p3RQgmpDcLT2pdQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.0.0-alpha.13", - "@typescript-eslint/types": "8.0.0-alpha.13", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", + "@typescript-eslint/scope-manager": "8.0.0-alpha.14", + "@typescript-eslint/types": "8.0.0-alpha.14", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.14", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", "debug": "^4.3.4" }, "engines": { @@ -2198,13 +2535,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.13.tgz", - "integrity": "sha512-5XA+tfDumd6Y5GWVFMF2810HB8GIijMawdLgMptSeN3CeOfxQB1J3EBtmbgSWuValNSvV6jujIxKRVgnrZ/WpA==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.14.tgz", + "integrity": "sha512-6EmhoNZzfjd/sZGxichVguWUGCCgT12xyw3ppNZ9bM/m6qQCE66BqudGxzD58UPL4PpN++Y8KqVItax0gNq4BQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.13", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.13" + "@typescript-eslint/types": "8.0.0-alpha.14", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.14" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2215,13 +2552,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.13.tgz", - "integrity": "sha512-4Rkwf4YaQIqeF/l2/F9hqxiWlr9P5a0M7Ep0kK99WDYDAhABraIgvsScd2PAtKU1smJXdozbqKDywKCg6etH5Q==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.14.tgz", + "integrity": "sha512-F/rtAXWMrFPO49xK0XLw7hYtPVrjj+jRJhJRRcSBWRybcu7rvlEQ/Chk+QXvyp15QuwmMD5jAqNI+Fkbxgc0gQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", - "@typescript-eslint/utils": "8.0.0-alpha.13", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.14", + "@typescript-eslint/utils": "8.0.0-alpha.14", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2239,9 +2576,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.13.tgz", - "integrity": "sha512-uJN8BSa8YHs9E/lFR9nkzLyaEijXGzMLgkh8DuQMlOP8epqJ9Jnzx+gOFUIyJV+C8uavpGp+YnCMvq4IDG9L+Q==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.14.tgz", + "integrity": "sha512-2u0FBQ0usELnbTqZhHN6X8ngJlpCchFTroWFG5nvo0TOoiPYV+5AbGiRb0IWMsLfxSzeDJeasUzByVvOHn1t1A==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2252,13 +2589,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.13.tgz", - "integrity": "sha512-p5SZlikPhHg4510ttfqwjwMgBG7ehzRj5dLk8JR5x9bV0D8zWNotqkWNa0vftbYHb+mdGV6D9zNrJwDjWVd4ag==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.14.tgz", + "integrity": "sha512-FM0qHSJ4Sqg49wBCcljq//J9V8SJbq3XFmjaWCF8Tk2hIuYkYZp7joXHs0Ld3FnM+9rj84OQTqSq8zczArNMNg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.13", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.13", + "@typescript-eslint/types": "8.0.0-alpha.14", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2304,18 +2641,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.13.tgz", - "integrity": "sha512-BMAbK/fC7RgvRjGQh8AixfQErFXIpCqL1DfFmMnp+oBZvYLON/4/iIj0yPPpY3E8gbljT7s4LHVkNSu7K/MQug==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.14.tgz", + "integrity": "sha512-hiH1uqRVyOPd+ZWqInwRob2s3Cq+p7LTIolvj+x7QJ6CpBCPrEMEPVuBiFibw2/rW+zJGTa3Ggjdpqy8bLb60g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "8.0.0-alpha.13", - "@typescript-eslint/types": "8.0.0-alpha.13", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.13", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "8.0.0-alpha.14", + "@typescript-eslint/types": "8.0.0-alpha.14", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.14" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2329,12 +2663,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.13.tgz", - "integrity": "sha512-q8r/RVc5AtAnnRGqKBIoWMwChET2hqydVGqAXlvXXnB6JOE3prpMLvnpUp7JK1jma3VPt2JieCGhzX3YZSLY/Q==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.14.tgz", + "integrity": "sha512-LwUhX8+ttlzJWhqLAkiH7E1tX2WJS0zvK0D83w4L9DRl4TRSQBuGtPIM1+GvG90VMix8sjlGaybBzWfNji1cUw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.13", + "@typescript-eslint/types": "8.0.0-alpha.14", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -10196,14 +10530,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.0.0-alpha.13", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.13.tgz", - "integrity": "sha512-DMFKGlZTYPGrjejz1G8Cp0LuvxlelQqy1BY/T95yNVK13aVYTnDrAZ5ytW9a79OzserqBDoJnXs1AFaUqJoNEg==", + "version": "8.0.0-alpha.14", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.14.tgz", + "integrity": "sha512-Un2y0pbBCdvmk2YsY/S/oftSA/4tEZtRMfewHlXJ43LBR07V2HSXPC/t6RJ29KZ+N5ORqe61QUQLupquVBPZhQ==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.0.0-alpha.13", - "@typescript-eslint/parser": "8.0.0-alpha.13", - "@typescript-eslint/utils": "8.0.0-alpha.13" + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.14", + "@typescript-eslint/parser": "8.0.0-alpha.14", + "@typescript-eslint/utils": "8.0.0-alpha.14" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 7f58e2ea..749be64a 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ }, "devDependencies": { "@eslint/js": "^9.3.0", - "@stylistic/eslint-plugin-js": "^2.1.0", + "@stylistic/eslint-plugin": "^2.1.0", "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", "eslint": "^9.3.0", @@ -101,6 +101,6 @@ "rimraf": "^5.0.7", "ts-node": "^10.9.2", "typescript": "^5.4.5", - "typescript-eslint": "^8.0.0-alpha.13" + "typescript-eslint": "^8.0.0-alpha.14" } } diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 58bf9e1f..f537541c 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -3,13 +3,15 @@ * blindtilt.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; -import { BlindTiltMappingMode } from '../utils.js'; +import { Devices } from '../settings.js'; import { interval, Subject } from 'rxjs'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; +import { BlindTiltMappingMode } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; export class BlindTilt extends deviceBase { diff --git a/src/device/bot.ts b/src/device/bot.ts index 60e475df..6b6581da 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -5,10 +5,12 @@ import { request } from 'undici'; import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory @@ -17,7 +19,7 @@ import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../se */ export class Bot extends deviceBase { // Services - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 1fc2c349..dc8b7da9 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -4,12 +4,14 @@ */ import { request } from 'undici'; import { deviceBase } from './device.js'; -import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { interval, Subject } from 'rxjs'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; +import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; -import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 511c1242..3699afa8 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -5,11 +5,13 @@ import { request } from 'undici'; import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; -import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; +import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory diff --git a/src/device/contact.ts b/src/device/contact.ts index 55d2ac36..634202e5 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -4,10 +4,12 @@ */ import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; +import { Devices } from '../settings.js'; import { skipWhile } from 'rxjs/operators'; -import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory @@ -16,11 +18,17 @@ import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../se */ export class Contact extends deviceBase { // Services - private ContactSensor!: { + private ContactSensor: { Service: Service; ContactSensorState: CharacteristicValue; }; + private Battery: { + Service: Service; + BatteryLevel: CharacteristicValue; + StatusLowBattery: CharacteristicValue; + }; + private MotionSensor?: { Service: Service; MotionDetected: CharacteristicValue; @@ -31,12 +39,6 @@ export class Contact extends deviceBase { CurrentAmbientLightLevel: CharacteristicValue; }; - private Battery!: { - Service: Service; - BatteryLevel: CharacteristicValue; - StatusLowBattery: CharacteristicValue; - }; - // Updates contactUpdateInProgress!: boolean; doContactUpdate!: Subject; @@ -57,6 +59,13 @@ export class Contact extends deviceBase { ContactSensorState: this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; + // Initialize Battery property + this.Battery = { + Service: accessory.getService(this.hap.Service.Battery) as Service, + BatteryLevel: 100, + StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + }; + // Initialize Motion Sensor property if (!this.device.contact?.hide_motionsensor) { this.MotionSensor = { diff --git a/src/device/curtain.ts b/src/device/curtain.ts index fa90d757..0349fd85 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -6,14 +6,16 @@ import { hostname } from 'os'; import { request } from 'undici'; import { interval, Subject } from 'rxjs'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge'; export class Curtain extends deviceBase { // Services - private WindowCovering!: { + private WindowCovering: { Service: Service; PositionState: CharacteristicValue; TargetPosition: CharacteristicValue; @@ -21,7 +23,7 @@ export class Curtain extends deviceBase { HoldPosition: CharacteristicValue; }; - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; diff --git a/src/device/device.ts b/src/device/device.ts index dc613eb0..082dfa72 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -2,15 +2,16 @@ * * device.ts: @switchbot/homebridge-switchbot. */ -import { API, HAP, Logging, PlatformAccessory } from 'homebridge'; import { hostname } from 'os'; -import { MqttClient } from 'mqtt'; import asyncmqtt from 'async-mqtt'; -import { SwitchBotPlatform } from '../platform.js'; -import { SwitchBotPlatformConfig, device, devicesConfig } from '../settings.js'; import { BlindTiltMappingMode, SwitchBotModel, SwitchBotBLEModel, sleep } from '../utils.js'; +import type { MqttClient } from 'mqtt'; +import type { SwitchBotPlatform } from '../platform.js'; +import type { API, HAP, Logging, PlatformAccessory } from 'homebridge'; +import type { SwitchBotPlatformConfig, device, devicesConfig } from '../settings.js'; + export abstract class deviceBase { public readonly api: API; public readonly log: Logging; diff --git a/src/device/fan.ts b/src/device/fan.ts index b4bc30f7..8a87fab0 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -4,10 +4,12 @@ */ import { request } from 'undici'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; export class Fan extends deviceBase { // Services diff --git a/src/device/hub.ts b/src/device/hub.ts index 551d5a47..ae95cde3 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -2,12 +2,15 @@ * * hub.ts: @switchbot/homebridge-switchbot. */ -import { Subject, interval, skipWhile } from 'rxjs'; +import { Units } from 'homebridge'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, device, deviceStatus, devicesConfig } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; +import { Devices } from '../settings.js'; import { convertUnits } from '../utils.js'; +import { Subject, interval, skipWhile } from 'rxjs'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { device, deviceStatus, devicesConfig } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; export class Hub extends deviceBase { // Services diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index ac0d1226..6f2c14d2 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -1,11 +1,13 @@ import { request } from 'undici'; import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; -import { SwitchBotPlatform } from '../platform.js'; -import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +import { Devices } from '../settings.js'; import { convertUnits } from '../utils.js'; +import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory @@ -158,7 +160,7 @@ export class Humidifier extends deviceBase { `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; if (!this.device.humidifier?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); } this.updateHomeKitCharacteristics(); } catch (e: any) { diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index c764d3bf..71c25145 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -2,12 +2,15 @@ * * iosensor.ts: @switchbot/homebridge-switchbot. */ +import { Units } from 'homebridge'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Subject, interval, skipWhile } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; +import { Devices } from '../settings.js'; import { convertUnits } from '../utils.js'; +import { Subject, interval, skipWhile } from 'rxjs'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory @@ -16,7 +19,7 @@ import { convertUnits } from '../utils.js'; */ export class IOSensor extends deviceBase { // Services - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 6e221343..e2deb451 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -1,11 +1,13 @@ import { request } from 'undici'; -import { interval, skipWhile, Subject } from 'rxjs'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; +import { interval, skipWhile, Subject } from 'rxjs'; import { debounceTime, take, tap } from 'rxjs/operators'; -import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; -import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; +import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** * Platform Accessory diff --git a/src/device/lock.ts b/src/device/lock.ts index 3d19b2a3..4f75b978 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -1,10 +1,12 @@ import { request } from 'undici'; import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; -import { SwitchBotPlatform } from '../platform.js'; +import { Devices } from '../settings.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, deviceStatus, Devices, serviceData } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js'; export class Lock extends deviceBase { // Services diff --git a/src/device/meter.ts b/src/device/meter.ts index c55fa4d3..48b30a01 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -2,16 +2,19 @@ * * meter.ts: @switchbot/homebridge-switchbot. */ +import { Units } from 'homebridge'; +import { Devices } from '../settings.js'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; import { convertUnits } from '../utils.js'; import { Subject, interval, skipWhile } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; export class Meter extends deviceBase { // Services - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 78262239..579e73f1 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -2,11 +2,14 @@ * * meterplus.ts: @switchbot/homebridge-switchbot. */ +import { Units } from 'homebridge'; +import { Devices } from '../settings.js'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; import { Subject, interval, skipWhile } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service, Units } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory @@ -15,7 +18,7 @@ import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../se */ export class MeterPlus extends deviceBase { // Services - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; diff --git a/src/device/motion.ts b/src/device/motion.ts index 8c1df48f..8fc9b2c1 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -3,11 +3,13 @@ * * motion.ts: @switchbot/homebridge-switchbot. */ +import { Devices } from '../settings.js'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; import { Subject, interval, skipWhile } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -16,13 +18,13 @@ import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../se */ export class Motion extends deviceBase { // Services - private Battery!: { + private Battery: { Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; - private MotionSensor!: { + private MotionSensor: { Service: Service; MotionDetected: CharacteristicValue; }; @@ -48,13 +50,13 @@ export class Motion extends deviceBase { // Initialize Motion Sensor property this.MotionSensor = { - Service: accessory.getService(this.hap.Service.MotionSensor) as Service, + Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected || false, }; // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel || 100, StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; @@ -71,12 +73,13 @@ export class Motion extends deviceBase { // Retrieve initial values and updateHomekit this.refreshStatus(); - // get the Battery service if it exists, otherwise create a new Motion service - // you can create multiple services for each accessory - (this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) - || accessory.addService(this.hap.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`; + /*(this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) + || accessory.addService(this.hap.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`;*/ + + if (this.MotionSensor.Service) { + this.MotionSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + } - this.MotionSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/MotionSensor @@ -97,12 +100,14 @@ export class Motion extends deviceBase { } // Battery Service - const BatteryService = `${accessory.displayName} Battery`; + /*const BatteryService = `${accessory.displayName} Battery`; (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; + || accessory.addService(this.hap.Service.Battery)), BatteryService;*/ - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + if (this.Battery.Service) { + this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); + this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + } // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); diff --git a/src/device/plug.ts b/src/device/plug.ts index 75812b33..7b74e005 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -3,11 +3,13 @@ * plug.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; export class Plug extends deviceBase { // Services private Outlet: { diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 79c2ec5a..b8234e90 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -3,12 +3,14 @@ * robotvacuumcleaner.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; -import { Subject, interval, skipWhile } from 'rxjs'; +import { Devices } from '../settings.js'; import { deviceBase } from './device.js'; -import { SwitchBotPlatform } from '../platform.js'; +import { Subject, interval, skipWhile } from 'rxjs'; import { debounceTime, take, tap } from 'rxjs/operators'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, deviceStatus, serviceData, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; export class RobotVacuumCleaner extends deviceBase { // Services @@ -594,7 +596,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; - this.Battery.Service?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // StatusLowBattery diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index e08d77ec..cca07397 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -4,10 +4,12 @@ */ import { deviceBase } from './device.js'; import { interval, Subject } from 'rxjs'; +import { Devices } from '../settings.js'; import { skipWhile } from 'rxjs/operators'; -import { SwitchBotPlatform } from '../platform.js'; -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import { device, devicesConfig, serviceData, deviceStatus, Devices } from '../settings.js'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; /** * Platform Accessory diff --git a/src/homebridge-ui/public/index.html b/src/homebridge-ui/public/index.html index bf5a8028..118c5647 100644 --- a/src/homebridge-ui/public/index.html +++ b/src/homebridge-ui/public/index.html @@ -1,8 +1,7 @@

homebridge-switchbot logo + alt="homebridge-switchbot logo" style="width: 50%" />

@@ -237,4 +243,4 @@
Help/About
homebridge.hideSpinner(); } })(); - + \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index d7d636b6..f1989218 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,9 +2,10 @@ * * index.ts: @switchbot/homebridge-switchbot plugin registration. */ -import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'; -import { API } from 'homebridge'; import { SwitchBotPlatform } from './platform.js'; +import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'; + +import type { API } from 'homebridge'; // Register our platform with homebridge. export default (api: API): void => { diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index b8ea9ed4..cf65019d 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -3,10 +3,12 @@ * airconditioners.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -607,7 +609,7 @@ export class AirConditioner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.HeaterCooler.Active}`); } else { this.accessory.context.Active = this.HeaterCooler.Active; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.HeaterCooler.Active); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.Active, this.HeaterCooler.Active); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.HeaterCooler.Active}`); } // RotationSpeed @@ -615,7 +617,7 @@ export class AirConditioner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); } else { this.accessory.context.RotationSpeed = this.HeaterCooler.RotationSpeed; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.HeaterCooler.RotationSpeed); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.HeaterCooler.RotationSpeed); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + ` RotationSpeed: ${this.HeaterCooler.RotationSpeed}`); } @@ -624,7 +626,7 @@ export class AirConditioner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentTemperature: ${this.HeaterCooler.CurrentTemperature}`); } else { this.accessory.context.CurrentTemperature = this.HeaterCooler.CurrentTemperature; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.HeaterCooler.CurrentTemperature); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.HeaterCooler.CurrentTemperature); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + ` CurrentTemperature: ${this.HeaterCooler.CurrentTemperature}`); } @@ -635,7 +637,7 @@ export class AirConditioner extends irdeviceBase { + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } else { this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor!.CurrentRelativeHumidity); this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` @@ -648,7 +650,7 @@ export class AirConditioner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); } else { this.accessory.context.TargetHeaterCoolerState = this.HeaterCooler.TargetHeaterCoolerState; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, this.HeaterCooler.TargetHeaterCoolerState); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState, this.HeaterCooler.TargetHeaterCoolerState); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + ` TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState}`); } @@ -658,7 +660,7 @@ export class AirConditioner extends irdeviceBase { + ` CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); } else { this.accessory.context.CurrentHeaterCoolerState = this.HeaterCooler.CurrentHeaterCoolerState; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.HeaterCooler.CurrentHeaterCoolerState); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.HeaterCooler.CurrentHeaterCoolerState); this.debugLog( `${this.device.remoteType}: ${this.accessory.displayName}` + ` updateCharacteristic CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`, @@ -669,8 +671,8 @@ export class AirConditioner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); } else { this.accessory.context.ThresholdTemperature = this.HeaterCooler.ThresholdTemperature; - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); - this.HeaterCooler.Service?.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); + this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, this.HeaterCooler.ThresholdTemperature); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + ` ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); } diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index b7742167..adbb13e9 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -3,10 +3,12 @@ * airpurifier.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index bf718eac..220fdeff 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -3,10 +3,12 @@ * camera.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -142,7 +144,7 @@ export class Camera extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Switch.On}`); } else { this.accessory.context.On = this.Switch.On; - this.Switch.Service?.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`); } } diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 4fffcc97..6be9d9e3 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -3,10 +3,12 @@ * fan.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -23,11 +25,6 @@ export class IRFan extends irdeviceBase { RotationDirection: CharacteristicValue; }; - // Config - minStep?: number; - minValue?: number; - maxValue?: number; - constructor( readonly platform: SwitchBotPlatform, accessory: PlatformAccessory, @@ -55,29 +52,15 @@ export class IRFan extends irdeviceBase { // handle on / off events using the Active characteristic this.Fan.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + if (device.irfan?.rotation_speed) { - if (device.irfan?.set_minStep) { - this.minStep = device.irfan?.set_minStep; - } else { - this.minStep = 1; - } - if (device.irfan?.set_min) { - this.minValue = device.irfan?.set_min; - } else { - this.minValue = 1; - } - if (device.irfan?.set_max) { - this.maxValue = device.irfan?.set_max; - } else { - this.maxValue = 100; - } // handle Rotation Speed events using the RotationSpeed characteristic this.Fan.Service .getCharacteristic(this.hap.Characteristic.RotationSpeed) .setProps({ - minStep: this.minStep, - minValue: this.minValue, - maxValue: this.maxValue, + minStep: Number(this.minStep(device)), + minValue: Number(this.minValue(device)), + maxValue: Number(this.maxValue(device)), }) .onSet(this.RotationSpeedSet.bind(this)); } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !device.irfan?.swing_mode) { @@ -106,6 +89,36 @@ export class IRFan extends irdeviceBase { } } + async minStep(device: irdevice & irDevicesConfig): Promise { + let minStep: number; + if (device.irfan?.set_minStep) { + minStep = device.irfan?.set_minStep; + } else { + minStep = 1; + } + return minStep; + } + + async minValue(device: irdevice & irDevicesConfig): Promise { + let minValue: number; + if (device.irfan?.set_min) { + minValue = device.irfan?.set_min; + } else { + minValue = 1; + } + return minValue; + } + + async maxValue(device: irdevice & irDevicesConfig): Promise { + let maxValue: number; + if (device.irfan?.set_max) { + maxValue = device.irfan?.set_max; + } else { + maxValue = 100; + } + return maxValue; + } + async SwingModeSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${value}`); if (value > this.Fan.SwingMode) { @@ -258,21 +271,21 @@ export class IRFan extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`); } else { this.accessory.context.Active = this.Fan.Active; - this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`); } if (this.Fan.SwingMode === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`); } else { this.accessory.context.SwingMode = this.Fan.SwingMode; - this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`); } if (this.Fan.RotationSpeed === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`); } else { this.accessory.context.RotationSpeed = this.Fan.RotationSpeed; - this.Fan.Service?.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`); } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 943a6b5d..d39ca885 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -1,11 +1,10 @@ /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * - * device.ts: homebridge-august. + * device.ts: @switchbot/homebridge-switchbot. */ -import { API, HAP, Logging, PlatformAccessory } from 'homebridge'; - -import { SwitchBotPlatform } from '../platform.js'; -import { SwitchBotPlatformConfig, irDevicesConfig, irdevice } from '../settings.js'; +import type { SwitchBotPlatform } from '../platform.js'; +import type { API, HAP, Logging, PlatformAccessory } from 'homebridge'; +import type { SwitchBotPlatformConfig, irDevicesConfig, irdevice } from '../settings.js'; export abstract class irdeviceBase { public readonly api: API; diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index bd3fbfdc..ff71f3d5 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -3,10 +3,12 @@ * light.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -15,20 +17,22 @@ import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; */ export class Light extends irdeviceBase { // Services - private LightBulb!: { + private LightBulb?: { Service: Service; On: CharacteristicValue; }; - ProgrammableSwitchServiceOn?: Service; - ProgrammableSwitchServiceOff?: Service; + private ProgrammableSwitchOn?: { + Service: Service; + ProgrammableSwitchEvent: CharacteristicValue; + ProgrammableSwitchOutputState: CharacteristicValue; + }; - // Characteristic Values - On!: CharacteristicValue; - ProgrammableSwitchEventOn?: CharacteristicValue; - ProgrammableSwitchOutputStateOn?: CharacteristicValue; - ProgrammableSwitchEventOff?: CharacteristicValue; - ProgrammableSwitchOutputStateOff?: CharacteristicValue; + private ProgrammableSwitchOff?: { + Service: Service; + ProgrammableSwitchEvent: CharacteristicValue; + ProgrammableSwitchOutputState: CharacteristicValue; + }; constructor( readonly platform: SwitchBotPlatform, @@ -40,7 +44,7 @@ export class Light extends irdeviceBase { if (!device.irlight?.stateless) { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, + Service: accessory.getService(this.hap.Service.Lightbulb)! as Service, On: accessory.context.On || false, }; // get the Light service if it exists, otherwise create a new Light service @@ -57,47 +61,47 @@ export class Light extends irdeviceBase { } else { // create a new Stateful Programmable Switch On service - const ProgrammableSwitchServiceOn = `${accessory.displayName} ${device.remoteType} On`; - (this.ProgrammableSwitchServiceOn = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchServiceOn; + const ProgrammableSwitchOn = `${accessory.displayName} ${device.remoteType} On`; + (this.ProgrammableSwitchOn!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchOn; - this.ProgrammableSwitchServiceOn.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} On`); + this.ProgrammableSwitchOn?.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} On`); - this.ProgrammableSwitchServiceOn.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ + this.ProgrammableSwitchOn?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ validValueRanges: [0, 0], minValue: 0, maxValue: 0, validValues: [0], }) .onGet(() => { - return this.ProgrammableSwitchEventOn!; + return this.ProgrammableSwitchOn!.ProgrammableSwitchEvent; }); - this.ProgrammableSwitchServiceOn.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + this.ProgrammableSwitchOn?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) .onSet(this.ProgrammableSwitchOutputStateSetOn.bind(this)); // create a new Stateful Programmable Switch Off service - const ProgrammableSwitchServiceOff = `${accessory.displayName} ${device.remoteType} Off`; - (this.ProgrammableSwitchServiceOff = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchServiceOff; + const ProgrammableSwitchOff = `${accessory.displayName} ${device.remoteType} Off`; + (this.ProgrammableSwitchOff!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchOff; - this.ProgrammableSwitchServiceOff.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Off`); + this.ProgrammableSwitchOff?.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Off`); - this.ProgrammableSwitchServiceOff.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ + this.ProgrammableSwitchOff?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ validValueRanges: [0, 0], minValue: 0, maxValue: 0, validValues: [0], }) .onGet(() => { - return this.ProgrammableSwitchEventOff!; + return this.ProgrammableSwitchOff!.ProgrammableSwitchEvent; }); - this.ProgrammableSwitchServiceOff.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + this.ProgrammableSwitchOff?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) .onSet(this.ProgrammableSwitchOutputStateSetOff.bind(this)); } @@ -106,11 +110,13 @@ export class Light extends irdeviceBase { async OnSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`); - this.On = value; - if (this.On) { - await this.pushLightOnChanges(); + this.LightBulb!.On = value; + if (this.LightBulb?.On) { + const On = true; + await this.pushLightOnChanges(On); } else { - await this.pushLightOffChanges(); + const On = false; + await this.pushLightOffChanges(On); } /** * pushLightOnChanges and pushLightOffChanges above assume they are measuring the state of the accessory BEFORE @@ -121,10 +127,10 @@ export class Light extends irdeviceBase { async ProgrammableSwitchOutputStateSetOn(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`); - this.ProgrammableSwitchOutputStateOn = value; - if (this.ProgrammableSwitchOutputStateOn === 1) { - this.On = true; - await this.pushLightOnChanges(); + this.ProgrammableSwitchOn!.ProgrammableSwitchOutputState = value; + if (this.ProgrammableSwitchOn?.ProgrammableSwitchOutputState === 1) { + const On = true; + await this.pushLightOnChanges(On); } /** * pushLightOnChanges and pushLightOffChanges above assume they are measuring the state of the accessory BEFORE @@ -135,10 +141,10 @@ export class Light extends irdeviceBase { async ProgrammableSwitchOutputStateSetOff(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`); - this.ProgrammableSwitchOutputStateOff = value; - if (this.ProgrammableSwitchOutputStateOff === 1) { - this.On = false; - await this.pushLightOffChanges(); + this.ProgrammableSwitchOff!.ProgrammableSwitchOutputState = value; + if (this.ProgrammableSwitchOff?.ProgrammableSwitchOutputState === 1) { + const On = false; + await this.pushLightOffChanges(On); } /** * pushLightOnChanges and pushLightOffChanges above assume they are measuring the state of the accessory BEFORE @@ -158,11 +164,9 @@ export class Light extends irdeviceBase { * Light - "command" "channelAdd" "default" = next channel * Light - "command" "channelSub" "default" = previous channel */ - async pushLightOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushLightOnChanges On: ${this.On},` + ` disablePushOn: ${this.disablePushOn}`, - ); - if (this.On && !this.disablePushOn) { + async pushLightOnChanges(On: boolean): Promise { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushLightOnChanges On: ${On}, disablePushOn: ${this.disablePushOn}`); + if (On === true && this.disablePushOn === false) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); const bodyChange = JSON.stringify({ @@ -170,15 +174,13 @@ export class Light extends irdeviceBase { parameter: 'default', commandType: commandType, }); - await this.pushChanges(bodyChange); + await this.pushChanges(bodyChange, On); } } - async pushLightOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges On: ${this.On},` + ` disablePushOff: ${this.disablePushOff}`, - ); - if (!this.On && !this.disablePushOff) { + async pushLightOffChanges(On: boolean): Promise { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges On: ${On}, disablePushOff: ${this.disablePushOff}`); + if (On === false && this.disablePushOff === false) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); const bodyChange = JSON.stringify({ @@ -186,29 +188,11 @@ export class Light extends irdeviceBase { parameter: 'default', commandType: commandType, }); - await this.pushChanges(bodyChange); + await this.pushChanges(bodyChange, On); } } - /*async pushLightBrightnessUpChanges(): Promise { - const bodyChange = JSON.stringify({ - command: 'brightnessUp', - parameter: 'default', - commandType: 'command', - }); - await this.pushChanges(bodyChange); - } - - async pushLightBrightnessDownChanges(): Promise { - const bodyChange = JSON.stringify({ - command: 'brightnessDown', - parameter: 'default', - commandType: 'command', - }); - await this.pushChanges(bodyChange); - }*/ - - async pushChanges(bodyChange: any): Promise { + async pushChanges(bodyChange: any, On: boolean): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushChanges`); if (this.device.connectionType === 'OpenAPI') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); @@ -227,7 +211,7 @@ export class Light extends irdeviceBase { + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); - this.accessory.context.On = this.On; + this.accessory.context.On = On; this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); @@ -235,63 +219,59 @@ export class Light extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName} Connection Type: ` + + `${this.device.connectionType}, commands will not be sent to OpenAPI`); } } async updateHomeKitCharacteristics(): Promise { - if (this.device.irlight?.stateless) { + if (!this.device.irlight?.stateless) { // On - if (this.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`); + if (this.LightBulb?.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.LightBulb?.On}`); } else { - this.accessory.context.On = this.On; - this.LightBulb.Service?.updateCharacteristic(this.hap.Characteristic.On, this.On); - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.accessory.context.On = this.LightBulb.On; + this.LightBulb?.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`); } } else { // On Stateful Programmable Switch - if (this.ProgrammableSwitchOutputStateOn === undefined) { + if (this.ProgrammableSwitchOn?.ProgrammableSwitchOutputState === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` ProgrammableSwitchOutputStateOn: ${this.ProgrammableSwitchOutputStateOn}`); + + ` ProgrammableSwitchOutputStateOn: ${this.ProgrammableSwitchOn?.ProgrammableSwitchOutputState}`); } else { - this.accessory.context.ProgrammableSwitchOutputStateOn = this.ProgrammableSwitchOutputStateOn; - this.ProgrammableSwitchServiceOn?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, - this.ProgrammableSwitchOutputStateOn); + this.accessory.context.ProgrammableSwitchOutputStateOn = this.ProgrammableSwitchOn.ProgrammableSwitchOutputState; + this.ProgrammableSwitchOn?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, + this.ProgrammableSwitchOn.ProgrammableSwitchOutputState); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchOutputStateOn: ${this.ProgrammableSwitchOutputStateOn}`); + + ` ProgrammableSwitchOutputStateOn: ${this.ProgrammableSwitchOn.ProgrammableSwitchOutputState}`); } // Off Stateful Programmable Switch - if (this.ProgrammableSwitchOutputStateOff === undefined) { + if (this.ProgrammableSwitchOff?.ProgrammableSwitchOutputState === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` ProgrammableSwitchOutputStateOff: ${this.ProgrammableSwitchOutputStateOff}`); + + ` ProgrammableSwitchOutputStateOff: ${this.ProgrammableSwitchOff?.ProgrammableSwitchOutputState}`); } else { - this.accessory.context.ProgrammableSwitchOutputStateOff = this.ProgrammableSwitchOutputStateOff; - this.ProgrammableSwitchServiceOff?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, - this.ProgrammableSwitchOutputStateOff); + this.accessory.context.ProgrammableSwitchOutputStateOff = this.ProgrammableSwitchOff.ProgrammableSwitchOutputState; + this.ProgrammableSwitchOff.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, + this.ProgrammableSwitchOff.ProgrammableSwitchOutputState); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchOutputStateOff: ${this.ProgrammableSwitchOutputStateOff}`); + + ` ProgrammableSwitchOutputStateOff: ${this.ProgrammableSwitchOff?.ProgrammableSwitchOutputState}`); } } } async apiError(e: any): Promise { - if (this.device.irlight?.stateless) { - this.LightBulb.Service?.updateCharacteristic(this.hap.Characteristic.On, e); + if (!this.device.irlight?.stateless) { + this.LightBulb?.Service.updateCharacteristic(this.hap.Characteristic.On, e); } else { - this.ProgrammableSwitchServiceOn?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.ProgrammableSwitchServiceOn?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); - this.ProgrammableSwitchServiceOff?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.ProgrammableSwitchServiceOff?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + this.ProgrammableSwitchOn?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.ProgrammableSwitchOn?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + this.ProgrammableSwitchOff?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.ProgrammableSwitchOff?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); } } } diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 095bec5d..98568a4a 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -3,10 +3,12 @@ * other.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 9e47838b..485f7946 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -3,10 +3,12 @@ * tv.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -418,7 +420,7 @@ export class TV extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Television.Active}`); } else { this.accessory.context.Active = this.Television.Active; - this.Television.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Television.Active); + this.Television.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Television.Active); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Television.Active}`); } // ActiveIdentifier @@ -426,7 +428,7 @@ export class TV extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${this.Television.ActiveIdentifier}`); } else { this.accessory.context.ActiveIdentifier = this.Television.ActiveIdentifier; - this.Television.Service?.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, this.Television.ActiveIdentifier); + this.Television.Service.updateCharacteristic(this.hap.Characteristic.ActiveIdentifier, this.Television.ActiveIdentifier); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + ` ActiveIdentifier: ${this.Television.ActiveIdentifier}`); } diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index 954b0185..ef2dca5a 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -3,10 +3,12 @@ * vacuumcleaner.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -133,7 +135,7 @@ export class VacuumCleaner extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Switch.On}`); } else { this.accessory.context.On = this.Switch.On; - this.Switch.Service?.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`); } } diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index c4b4b551..3518bece 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -3,10 +3,12 @@ * waterheater.ts: @switchbot/homebridge-switchbot. */ import { request } from 'undici'; +import { Devices } from '../settings.js'; import { irdeviceBase } from './irdevice.js'; -import { SwitchBotPlatform } from '../platform.js'; -import { Devices, irDevicesConfig, irdevice } from '../settings.js'; -import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; + +import type { SwitchBotPlatform } from '../platform.js'; +import type { irDevicesConfig, irdevice } from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; /** * Platform Accessory @@ -138,7 +140,7 @@ export class WaterHeater extends irdeviceBase { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Valve.Active}`); } else { this.accessory.context.Active = this.Valve.Active; - this.Valve.Service?.updateCharacteristic(this.hap.Characteristic.Active, this.Valve.Active); + this.Valve.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Valve.Active); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Valve.Active}`); } } diff --git a/src/platform.ts b/src/platform.ts index 79e36457..22400052 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -2,24 +2,23 @@ * * platform.ts: @switchbot/homebridge-switchbot platform class. */ -import { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge'; -import { PLATFORM_NAME, PLUGIN_NAME, irdevice, device, SwitchBotPlatformConfig, devicesConfig, irDevicesConfig, Devices } from './settings.js'; +import { Hub } from './device/hub.js'; import { Bot } from './device/bot.js'; import { Plug } from './device/plug.js'; import { Lock } from './device/lock.js'; import { Meter } from './device/meter.js'; import { Motion } from './device/motion.js'; -import { Hub } from './device/hub.js'; import { Contact } from './device/contact.js'; import { Curtain } from './device/curtain.js'; import { IOSensor } from './device/iosensor.js'; -import { WaterDetector } from './device/waterdetector.js'; import { MeterPlus } from './device/meterplus.js'; import { ColorBulb } from './device/colorbulb.js'; -import { CeilingLight } from './device/ceilinglight.js'; import { StripLight } from './device/lightstrip.js'; import { Humidifier } from './device/humidifier.js'; +import { CeilingLight } from './device/ceilinglight.js'; +import { WaterDetector } from './device/waterdetector.js'; import { RobotVacuumCleaner } from './device/robotvacuumcleaner.js'; + import { Fan } from './device/fan.js'; import { TV } from './irdevice/tv.js'; import { IRFan } from './irdevice/fan.js'; @@ -31,18 +30,25 @@ import { AirPurifier } from './irdevice/airpurifier.js'; import { WaterHeater } from './irdevice/waterheater.js'; import { VacuumCleaner } from './irdevice/vacuumcleaner.js'; import { AirConditioner } from './irdevice/airconditioner.js'; -import * as http from 'http'; + import { Buffer } from 'buffer'; -import { Dispatcher, request } from 'undici'; -import { MqttClient } from 'mqtt'; +import { request } from 'undici'; +import asyncmqtt from 'async-mqtt'; +import { sleep } from './utils.js'; +import { createServer } from 'http'; import { queueScheduler } from 'rxjs'; import fakegato from 'fakegato-history'; -import asyncmqtt from 'async-mqtt'; import crypto, { randomUUID } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes'; -import { UrlObject } from 'url'; -import { sleep } from './utils.js'; +import { PLATFORM_NAME, PLUGIN_NAME, Devices, setupWebhook, updateWebhook, deleteWebhook, queryWebhook } from './settings.js'; + +import type { UrlObject } from 'url'; +import type { MqttClient } from 'mqtt'; +import type { Dispatcher } from 'undici'; +import type { Server, IncomingMessage, ServerResponse } from 'http'; +import type { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge'; +import type { irdevice, device, SwitchBotPlatformConfig, devicesConfig, irDevicesConfig } from './settings.js'; /** * HomebridgePlatform @@ -63,7 +69,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { platformLogging!: SwitchBotPlatformConfig['logging']; config!: SwitchBotPlatformConfig; - webhookEventListener: http.Server | null = null; + webhookEventListener: Server | null = null; mqttClient: MqttClient | null = null; public readonly fakegatoAPI: any; @@ -121,7 +127,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (this.config.credentials?.openToken && !this.config.credentials.token) { await this.updateToken(); } else if (this.config.credentials?.token && !this.config.credentials?.secret) { - // eslint-disable-next-line no-useless-escape + this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.'); } else { this.discoverDevices(); @@ -175,7 +181,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { const xurl = new URL(url); const port = Number(xurl.port); const path = xurl.pathname; - this.webhookEventListener = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => { + this.webhookEventListener = createServer((request: IncomingMessage, response: ServerResponse) => { try { if (request.url === path && request.method === 'POST') { request.on('data', async (data) => { @@ -212,16 +218,15 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } try { - const { body, statusCode } = await request( - 'https://api.switch-bot.com/v1.1/webhook/setupWebhook', { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - 'action': 'setupWebhook', - 'url': url, - 'deviceList': 'ALL', - }), - }); + const { body, statusCode } = await request(setupWebhook, { + method: 'POST', + headers: this.generateHeaders(), + body: JSON.stringify({ + 'action': 'setupWebhook', + 'url': url, + 'deviceList': 'ALL', + }), + }); const response: any = await body.json(); this.debugLog(`setupWebhook: url:${url}`); this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`); @@ -235,18 +240,15 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } try { - const { body, statusCode } = await request( - 'https://api.switch-bot.com/v1.1/webhook/updateWebhook', { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - 'action': 'updateWebhook', - 'config': { - 'url': url, - 'enable': true, - }, - }), - }); + const { body, statusCode } = await request(updateWebhook, { + method: 'POST', headers: this.generateHeaders(), body: JSON.stringify({ + 'action': 'updateWebhook', + 'config': { + 'url': url, + 'enable': true, + }, + }), + }); const response: any = await body.json(); this.debugLog(`updateWebhook: url:${url}`); this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`); @@ -259,14 +261,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } try { - const { body, statusCode } = await request( - 'https://api.switch-bot.com/v1.1/webhook/queryWebhook', { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - 'action': 'queryUrl', - }), - }); + const { body, statusCode } = await request(queryWebhook, { + method: 'POST', + headers: this.generateHeaders(), + body: JSON.stringify({ + 'action': 'queryUrl', + }), + }); const response: any = await body.json(); this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`); this.debugLog(`queryWebhook: statusCode:${statusCode}`); @@ -281,15 +282,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.api.on('shutdown', async () => { try { - const { body, statusCode } = await request( - 'https://api.switch-bot.com/v1.1/webhook/deleteWebhook', { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - 'action': 'deleteWebhook', - 'url': url, - }), - }); + const { body, statusCode } = await request(deleteWebhook, { + method: 'POST', + headers: this.generateHeaders(), + body: JSON.stringify({ + 'action': 'deleteWebhook', + 'url': url, + }), + }); const response: any = await body.json(); this.debugLog(`deleteWebhook: url:${url}`); this.debugLog(`deleteWebhook: body:${JSON.stringify(response)}`); @@ -449,18 +449,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } // Move openToken to token if (!this.config.credentials.secret) { - // eslint-disable-next-line no-useless-escape, max-len - this.warnLog( - 'This plugin has been updated to use OpenAPI v1.1, config is set with openToken, "openToken" cconfig has been moved to the "token" config', - ); - // eslint-disable-next-line no-useless-escape + this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken,' + + ' "openToken" cconfig has been moved to the "token" config'); this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.'); } else { - // eslint-disable-next-line no-useless-escape, max-len - this.warnLog( - 'This plugin has been updated to use OpenAPI v1.1, config is set with openToken, ' - + '"openToken" config has been moved to the "token" config, please restart Homebridge.', - ); + this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, ' + + '"openToken" config has been moved to the "token" config, please restart Homebridge.'); } // set the refresh token @@ -754,9 +748,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.createFan(device); break; default: - this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`); - // eslint-disable-next-line max-len - this.warnLog('Submit Feature Requests Here: ' + 'https://tinyurl.com/SwitchBotFeatureRequest'); + this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`, + + 'Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest'); } } diff --git a/src/settings.ts b/src/settings.ts index 43b502cd..acd97c68 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2,9 +2,8 @@ * * settings.ts: @switchbot/homebridge-switchbot platform class. */ -/* eslint-disable max-len */ -import { MacAddress, PlatformConfig } from 'homebridge'; -import { IClientOptions } from 'async-mqtt'; +import type { IClientOptions } from 'async-mqtt'; +import type { MacAddress, PlatformConfig } from 'homebridge'; /** * This is the name of the platform that users will use to register the plugin in the Homebridge config.json */ @@ -20,6 +19,26 @@ export const PLUGIN_NAME = '@switchbot/homebridge-switchbot'; */ export const Devices = 'https://api.switch-bot.com/v1.1/devices'; +/** + * This is the updateWebhook url used to access SwitchBot API + */ +export const setupWebhook = 'https://api.switch-bot.com/v1.1/webhook/setupWebhook'; + +/** + * This is the updateWebhook url used to access SwitchBot API + */ +export const queryWebhook = 'https://api.switch-bot.com/v1.1/webhook/queryWebhook'; + +/** + * This is the updateWebhook url used to access SwitchBot API + */ +export const updateWebhook = 'https://api.switch-bot.com/v1.1/webhook/updateWebhook'; + +/** + * This is the deleteWebhook url used to access SwitchBot API + */ +export const deleteWebhook = 'https://api.switch-bot.com/v1.1/webhook/deleteWebhook'; + //Config export interface SwitchBotPlatformConfig extends PlatformConfig { credentials?: credentials; @@ -39,8 +58,8 @@ export type options = { maxRetries?: number; delayBetweenRetries?: number; logging?: string; - devices?: Array; - irdevices?: Array; + devices?: devicesConfig[]; + irdevices?: irDevicesConfig[]; webhookURL?: string; mqttURL?: string; mqttOptions?: IClientOptions; @@ -245,7 +264,7 @@ export type body = { //a list of physical devices. export type deviceList = { - device: Array; + device: device[]; }; export type device = { @@ -260,9 +279,9 @@ export type device = { //device's parent Hub ID. hubDeviceId: string; //only available for Curtain devices. a list of Curtain device IDs such that the Curtain devices are being paired or grouped. - curtainDevicesIds?: Array; + curtainDevicesIds?: string[]; //only available for Blind Titl devices. a list of Blind Tilt device IDs such that the Blind Tilt devices are being paired or grouped. - blindTiltDevicesIds?: Array; + blindTiltDevicesIds?: string[]; //only available for Curtain/Lock devices. determines if the open position and the close position of a device have been properly calibrated or not calibrate?: boolean; //only available for Curtain devices. determines if a Curtain is paired with or grouped with another Curtain or not. @@ -295,13 +314,9 @@ export type device = { fanSpeed: number; }; -// values defined but not displayed by API -export interface deviceInfo extends device { -} - //a list of virtual infrared remote devices. export type infraredRemoteList = { - device: Array; + device: irdevice[]; }; export type irdevice = { @@ -320,51 +335,51 @@ export type deviceStatus = { export type deviceStatusBody = { //v1.1 of API - deviceId: string; //device ID. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt, Battery Circulator Fan) - deviceType: string; //device type. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) - hubDeviceId: string; //device's parent Hub ID. 000000000000 when the device itself is a Hub or it is connected through Wi-Fi. (Used by the following deviceTypes: Bot, Curtain, Meter, Meter Plus, Lock, Keypad, Keypad Touch, Motion Sensor, Contact Sensor, Ceiling Light, Ceiling Light Pro, Plug Mini (JP), Strip Light, Color Bulb, Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Humidifier, Blind Tilt) - power?: string; //ON/OFF state. (Used by the following deviceTypes: Bot, Ceiling Light, Ceiling Light Pro, PLug, Plug Mini (US), Plug Mini (JP), Strip Light, Color Bulb, Humidifier) - calibrate?: boolean; //determines if device has been calibrated or not. (Used by the following deviceTypes: Curtain, Lock, Blind Tilt) - group?: boolean; //determines if a device is paired with or grouped with another device or not. (Used by the following deviceTypes: Curtain, Blind Tilt) - moving?: boolean; //determines if a device is moving or not. (Used by the following deviceTypes: Curtain, Blind Tilt) - slidePosition?: number; //the current position (0-100) the percentage of the distance between the calibrated open position and closed position. (Used by the following deviceTypes: Curtain, Blind Tilt) - temperature?: number; //temperature in celsius (Used by the following deviceTypes: Meter, Meter Plus, Humidifier, IOSensor) - humidity?: number; //humidity percentage. (Used by the following deviceTypes: Meter, Meter Plus, Humidifier, IOSensor) - lockState?: string; //determines if locked or not. (Used by the following deviceTypes: Lock) - doorState?: string; //determines if the door is closed or not. (Used by the following deviceTypes: Lock) - moveDetected?: boolean; //determines if motion is detected. (Used by the following deviceTypes: Motion Sensor, Contact Sensor) - brightness?: string | number; //the ambient brightness picked up by the sensor. bright or dim. (Used by the following deviceTypes: Motion Sensor, Contact Sensor) | the brightness value, range from 1 to 100. (Used by the following deviceTypes: Ceiling Light, Ceiling Light Pro, Strip Light, Color Bulb) - openState?: string; //the open state of the sensor. open, close, or timeOutNotClose. (Used by the following deviceTypes: Contact Sensor) - colorTemperature?: number; //the color temperature value, range from 2700 to 6500. (Used by the following deviceTypes: Ceiling Light, Ceiling Light Pro, Color Bulb) - voltage?: number; //the voltage of the device, measured in Volt. (Used by the following deviceTypes: Plug Mini (US), Plug Mini (JP)) - weight?: number; //the power consumed in a day, measured in Watts. (Used by the following deviceTypes: Plug Mini (US), Plug Mini (JP)) - electricityOfDay?: number; //the duration that the device has been used during a day, measured in minutes. (Used by the following deviceTypes: Plug Mini (US), Plug Mini (JP)) - electricCurrent?: number; //the current of the device at the moment, measured in Amp. (Used by the following deviceTypes: Plug Mini (US), Plug Mini (JP)) - color?: string; //the color value, RGB "255:255:255". (Used by the following deviceTypes: Strip Light, Color Bulb) - workingStatus?: string; //the working status of the device. StandBy, Clearing, Paused, GotoChargeBase, Charging, ChargeDone, Dormant, InTrouble, InRemoteControl, or InDustCollecting. (Used by the following deviceTypes: Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus) - onlineStatus?: string; //the connection status of the device. online or offline. (Used by the following deviceTypes: Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus) - battery?: number; //the current battery level. (Used by the following deviceTypes: Robot Vacuum Cleaner S1, Robot Vacuum Cleaner S1 Plus, Blind Tilt, IOSensor) - deviceName?: string; //device name. (Used by the following deviceTypes: Robot Vacuum Cleaner S1 Plus) - nebulizationEfficiency?: number; //atomization efficiency percentage. (Used by the following deviceTypes: Humidifier) - auto?: boolean; //determines if a Humidifier is in Auto Mode or not. (Used by the following deviceTypes: Humidifier) - childLock?: boolean; //determines if a Humidifier's safety lock is on or not. (Used by the following deviceTypes: Humidifier) - sound?: boolean; //determines if a Humidifier is muted or not. (Used by the following deviceTypes: Humidifier) - lackWater?: boolean; //determines if the water tank is empty or not. (Used by the following deviceTypes: Humidifier) - version?: number; //the version of the device. - direction?: string; //the opening direction of a Blind Tilt device. (Used by the following deviceTypes: Blind Tilt) - runStatus?: string; //'static' when not moving. (Used by the following deviceTypes: Blind Tilt) - mode?: number | string; //available for devices. the fan mode. (Used by the following deviceTypes: Smart Fan, Battery Circulator Fan):(direct mode: direct; natural mode: "natural"; sleep mode: "sleep"; ultra quiet mode: "baby") - speed?: number; //the fan speed. (Used by the following deviceTypes: Smart Fan) - shaking?: boolean; //determines if the fan is swinging or not. (Used by the following deviceTypes: Smart Fan) - shakeCenter?: string; //the fan's swing direction. (Used by the following deviceTypes: Smart Fan) - shakeRange?: string; //the fan's swing range, 0~120°. (Used by the following deviceTypes: Smart Fan) - status?: number //the leak status. 0 for no leak, 1 for leak. (Used by the following deviceTypes: Water Detector) - lightLevel?: number; //the light level. (Used by the following deviceTypes: Hub) - nightStatus: number // set nightlight status. turn off: off; mode 1: 1; mode 2: 2 - oscillation: string // set horizontal oscillation. turn on: on; turn off: off - verticalOscillation: string // set vertical oscillation. turn on: on; turn off: off - chargingStatus: string // battery charge status. charging or uncharged - fanSpeed: number // fan speed. 1~100 + deviceId: string; + deviceType: string; + hubDeviceId: string; + power?: string; + calibrate?: boolean; + group?: boolean; + moving?: boolean; + slidePosition?: number; + temperature?: number; + humidity?: number; + lockState?: string; + doorState?: string; + moveDetected?: boolean; + brightness?: string | number; + openState?: string; + colorTemperature?: number; + voltage?: number; + weight?: number; + electricityOfDay?: number; + electricCurrent?: number; + color?: string; + workingStatus?: string; + onlineStatus?: string; + battery?: number; + deviceName?: string; + nebulizationEfficiency?: number; + auto?: boolean; + childLock?: boolean; + sound?: boolean; + lackWater?: boolean; + version?: number; + direction?: string; + runStatus?: string; + mode?: number | string; + speed?: number; + shaking?: boolean; + shakeCenter?: string; + shakeRange?: string; + status?: number; + lightLevel?: number; + nightStatus: number; + oscillation: string; + verticalOscillation: string; + chargingStatus: string; + fanSpeed: number; }; export type ad = { From 43617bb4b031847b29fdbc5d3829fff1b4a33a99 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 19 May 2024 18:03:48 -0500 Subject: [PATCH 61/73] add Name Services --- src/device/blindtilt.ts | 199 ++++++++------- src/device/bot.ts | 405 ++++++++++++++++++------------- src/device/ceilinglight.ts | 85 +++---- src/device/colorbulb.ts | 75 +++--- src/device/contact.ts | 137 ++++++----- src/device/curtain.ts | 167 ++++++++----- src/device/device.ts | 16 +- src/device/fan.ts | 87 +++++-- src/device/hub.ts | 104 ++++---- src/device/humidifier.ts | 100 ++++---- src/device/iosensor.ts | 104 ++++---- src/device/lightstrip.ts | 72 +++--- src/device/lock.ts | 139 ++++++----- src/device/meter.ts | 97 ++++---- src/device/meterplus.ts | 97 ++++---- src/device/motion.ts | 95 ++++---- src/device/plug.ts | 29 ++- src/device/robotvacuumcleaner.ts | 183 +++++++------- src/device/waterdetector.ts | 72 +++--- 19 files changed, 1219 insertions(+), 1044 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index f537541c..693ac99d 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -11,12 +11,13 @@ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; export class BlindTilt extends deviceBase { // Services private WindowCovering: { + Name: CharacteristicValue; Service: Service; PositionState: CharacteristicValue; TargetPosition: CharacteristicValue; @@ -26,12 +27,15 @@ export class BlindTilt extends deviceBase { }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; + ChargingState?: CharacteristicValue; }; private LightSensor?: { + Name: CharacteristicValue; Service: Service; CurrentAmbientLightLevel?: CharacteristicValue; }; @@ -62,70 +66,47 @@ export class BlindTilt extends deviceBase { this.blindTiltUpdateInProgress = false; this.setNewTarget = false; - // Initialize LightBulb property + // Initialize WindowCovering Service this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering) as Service, - PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, - TargetPosition: accessory.context.TargetPosition || 100, - CurrentPosition: accessory.context.CurrentPosition || 100, - TargetHorizontalTiltAngle: accessory.context.TargetHorizontalTiltAngle || 0, - CurrentHorizontalTiltAngle: accessory.context.CurrentHorizontalTiltAngle || 0, + Name: accessory.context.WindowCovering.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, + PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: accessory.context.TargetPosition ?? 100, + CurrentPosition: accessory.context.CurrentPosition ?? 100, + TargetHorizontalTiltAngle: accessory.context.TargetHorizontalTiltAngle ?? 90, + CurrentHorizontalTiltAngle: accessory.context.CurrentHorizontalTiltAngle ?? 90, }; - // Initialize Battery property - this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - }; - - // Initialize LightSensor property - if (!this.device.blindTilt?.hide_lightsensor) { - this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, - }; - } - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the WindowCovering service if it exists, otherwise create a new WindowCovering service - // you can create multiple services for each accessory - (this.WindowCovering!.Service = - accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), accessory.displayName; - - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // create handlers for required characteristics - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); - - this.WindowCovering!.Service - .getCharacteristic(this.hap.Characteristic.CurrentPosition) + // Initialize WindowCovering Characteristics + this.WindowCovering.Service + .setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name) + .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ - minStep: this.minStep(device), + minStep: Number(this.minStep(device)), minValue: 0, maxValue: 100, validValueRanges: [0, 100], }) .onGet(() => { - return this.WindowCovering?.CurrentPosition ?? 0; - }); + return this.WindowCovering.TargetPosition; + }) + .onSet(this.TargetPositionSet.bind(this)); - this.WindowCovering!.Service - .getCharacteristic(this.hap.Characteristic.TargetPosition) + // Initialize WindowCovering CurrentPosition Characteristic + this.WindowCovering.Service + .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ - minStep: this.minStep(device), + minStep: Number(this.minStep(device)), minValue: 0, maxValue: 100, validValueRanges: [0, 100], - }) - .onSet(this.TargetPositionSet.bind(this)); + }).onGet(() => { + return this.WindowCovering?.CurrentPosition ?? 0; + }); - this.WindowCovering!.CurrentHorizontalTiltAngle = 90; - this.WindowCovering!.Service - .getCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle) + // Initialize WindowCovering TargetHorizontalTiltAngle Characteristic + this.WindowCovering.Service + .getCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle) .setProps({ minStep: 180, minValue: -90, @@ -133,27 +114,63 @@ export class BlindTilt extends deviceBase { validValues: [-90, 90], }) .onGet(() => { - // this.debugLog(`requested CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); - return this.WindowCovering.CurrentHorizontalTiltAngle ?? 0; - }); + return this.WindowCovering.TargetHorizontalTiltAngle; + }) + .onSet(this.TargetHorizontalTiltAngleSet.bind(this)); + accessory.context.WindowCovering.Name = this.WindowCovering.Name; - this.WindowCovering!.TargetHorizontalTiltAngle = 90; - this.WindowCovering!.Service - .getCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle) + // Initialize WindowCovering CurrentHorizontalTiltAngle Characteristic + this.WindowCovering.Service + .getCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle) .setProps({ minStep: 180, minValue: -90, maxValue: 90, validValues: [-90, 90], - }) - .onSet(this.TargetHorizontalTiltAngleSet.bind(this)); + }).onGet(() => { + return this.WindowCovering.CurrentHorizontalTiltAngle ?? 0; + }); - // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + // Initialize Battery Service + this.Battery = { + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, + }; - this.Battery!.Service.setCharacteristic(this.hap.Characteristic.Name, batteryService); + // Initialize Battery Name Characteristic + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + accessory.context.Battery.Name = this.Battery.Name; + + // Initialize LightSensor Service + if (device.blindTilt?.hide_lightsensor) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor!.Service); + } else { + this.LightSensor = { + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, + }; + + // Initialize LightSensor Characteristics + this.LightSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel) + .onGet(() => { + return this.LightSensor?.CurrentAmbientLightLevel ?? 0.0001; + }); + accessory.context.LightSensor.Name = this.LightSensor.Name; + } + + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); @@ -167,19 +184,21 @@ export class BlindTilt extends deviceBase { //regisiter webhook event handler if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { slidePosition, battery } = context; + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { slidePosition, battery, lightLevel } = context; const { CurrentPosition } = this.WindowCovering; const { BatteryLevel } = this.Battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(slidePosition, battery) = ' + - `Webhook:(${slidePosition}, ${battery}), ` + - `current:(${CurrentPosition}, ${BatteryLevel})`); - this.WindowCovering!.CurrentPosition = slidePosition; + const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (slidePosition, battery, lightLevel) = ` + + `Webhook:(${slidePosition}, ${battery}, ${lightLevel}), current:(${CurrentPosition}, ${BatteryLevel}, ${CurrentAmbientLightLevel})`); + this.WindowCovering.CurrentPosition = slidePosition; this.Battery.BatteryLevel = battery; + if (!device.blindTilt?.hide_lightsensor) { + this.LightSensor!.CurrentAmbientLightLevel = lightLevel; + } this.updateHomeKitCharacteristics(); } catch (e: any) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` @@ -192,11 +211,11 @@ export class BlindTilt extends deviceBase { interval(this.deviceUpdateRate * 1000) //.pipe(skipWhile(() => this.blindTiltUpdateInProgress)) .subscribe(async () => { - if (this.WindowCovering!.PositionState === this.hap.Characteristic.PositionState.STOPPED) { + if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) { return; } - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,` - + ` PositionState: ${this.WindowCovering!.PositionState}`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} Refresh Status When Moving,` + + ` PositionState: ${this.WindowCovering.PositionState}`); await this.refreshStatus(); }); @@ -214,10 +233,8 @@ export class BlindTilt extends deviceBase { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.blindTiltUpdateInProgress = false; }); @@ -846,6 +863,17 @@ export class BlindTilt extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } + if (this.Battery.ChargingState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`); + } else { + if (this.device.mqttURL) { + this.mqttPublish('ChargingState', this.Battery.ChargingState.toString()); + } + this.accessory.context.ChargingState = this.Battery.ChargingState; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ChargingState: ${this.Battery.ChargingState}`); + } } async BLEPushConnection() { @@ -910,7 +938,7 @@ export class BlindTilt extends deviceBase { } } - minStep(device: device & devicesConfig): number { + async minStep(device: device & devicesConfig): Promise { let set_minStep: number; if (device.blindTilt?.set_minStep) { set_minStep = device.blindTilt?.set_minStep; @@ -945,6 +973,8 @@ export class BlindTilt extends deviceBase { this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle, 90); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle, 90); } } @@ -952,14 +982,15 @@ export class BlindTilt extends deviceBase { this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e); if (!this.device.curtain?.hide_lightsensor) { this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); } - if (this.BLE) { - this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - } - //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); } /** diff --git a/src/device/bot.ts b/src/device/bot.ts index 6b6581da..6d930817 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -10,7 +10,7 @@ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -20,57 +20,68 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Bot extends deviceBase { // Services private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private Switch?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private GarageDoor?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Door?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Window?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private WindowCovering?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; - private Lock?: { + private LockMechanism?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Faucet?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Fan?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private StatefulProgrammableSwitch?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Outlet?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -102,18 +113,41 @@ export class Bot extends deviceBase { // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + accessory.context.Battery.Name = this.Battery.Name; + // deviceType if (this.botDeviceType === 'switch') { - // Initialize Switch property + // Initialize Switch Service this.Switch = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`); + + // Initialize Switch Characteristics + this.Switch.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) + .setCharacteristic(this.hap.Characteristic.On, this.Switch.On) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Switch!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Switch.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -123,21 +157,32 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add switchService - const switchService = `${accessory.displayName} Switch`; - (this.Switch!.Service = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), switchService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`); - - this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'garagedoor') { - // Initialize Switch property + // Initialize GarageDoor Service this.GarageDoor = { - Service: accessory.getService(this.hap.Service.GarageDoorOpener) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`); + + // Initialize GarageDoor Characteristics + this.GarageDoor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.GarageDoor.Name) + .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false) + .getCharacteristic(this.hap.Characteristic.TargetDoorState).setProps({ + validValues: [0, 100], + minValue: 0, + maxValue: 100, + minStep: 100, + }) + .onGet(() => { + return this.GarageDoor!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.GarageDoor.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -147,22 +192,32 @@ export class Bot extends deviceBase { this.removeWindowService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add garageDoorService - const garageDoorService = `${accessory.displayName} Garage Door Opener`; - (this.GarageDoor!.Service = accessory.getService(this.hap.Service.GarageDoorOpener) - || accessory.addService(this.hap.Service.GarageDoorOpener)), garageDoorService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`); - - this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.GarageDoor!.Service.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); - this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); } else if (this.botDeviceType === 'door') { - // Initialize Switch property + // Initialize Door Service this.Door = { - Service: accessory.getService(this.hap.Service.Door) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`); + + // Initialize Door Characteristics + this.Door.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Door.Name) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) + .getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ + validValues: [0, 100], + minValue: 0, + maxValue: 100, + minStep: 100, + }) + .onGet(() => { + return this.Door!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Door.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeOutletService(accessory); @@ -172,30 +227,32 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); + } else if (this.botDeviceType === 'window') { + // Initialize Window Service + this.Window = { + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service, + On: accessory.context.On ?? false, + }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`); - // Add doorService - const doorService = `${accessory.displayName} Door`; - (this.Door!.Service = accessory.getService(this.hap.Service.Door) - || accessory.addService(this.hap.Service.Door)), doorService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`); - - this.Door!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Door!.Service - .getCharacteristic(this.hap.Characteristic.TargetPosition) - .setProps({ + // Initialize Window Characteristics + this.Window.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Window.Name) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) + .getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ validValues: [0, 100], minValue: 0, maxValue: 100, minStep: 100, }) + .onGet(() => { + return this.Window!.On; + }) .onSet(this.OnSet.bind(this)); - this.Door!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - } else if (this.botDeviceType === 'window') { - // Initialize Switch property - this.Window = { - Service: accessory.getService(this.hap.Service.Window) as Service, - On: accessory.context.On || false, - }; + accessory.context.Name = this.Window.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -205,30 +262,32 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); + } else if (this.botDeviceType === 'windowcovering') { + // Initialize WindowCovering Service + this.WindowCovering = { + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, + On: accessory.context.On ?? false, + }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`); - // Add windowService - const windowService = `${accessory.displayName} Window`; - (this.Window!.Service = accessory.getService(this.hap.Service.Window) - || accessory.addService(this.hap.Service.Window)), windowService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`); - - this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Window!.Service - .getCharacteristic(this.hap.Characteristic.TargetPosition) - .setProps({ + // Initialize WindowCovering Characteristics + this.WindowCovering.Service + .setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) + .getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ validValues: [0, 100], minValue: 0, maxValue: 100, minStep: 100, }) + .onGet(() => { + return this.WindowCovering!.On; + }) .onSet(this.OnSet.bind(this)); - this.Window!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - } else if (this.botDeviceType === 'windowcovering') { - // Initialize Switch property - this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering) as Service, - On: accessory.context.On || false, - }; + accessory.context.Name = this.WindowCovering.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -238,30 +297,26 @@ export class Bot extends deviceBase { this.removeWindowService(accessory); this.removeGarageDoorService(accessory); this.removeStatefulProgrammableSwitchService(accessory); + } else if (this.botDeviceType === 'lock') { + // Initialize Lock Service + this.LockMechanism = { + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, + On: accessory.context.On ?? false, + }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`); - // Add windowCoveringService - const windowCoveringService = `${accessory.displayName} Window Covering`; - (this.WindowCovering!.Service = accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`); - - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.WindowCovering!.Service - .getCharacteristic(this.hap.Characteristic.TargetPosition) - .setProps({ - validValues: [0, 100], - minValue: 0, - maxValue: 100, - minStep: 100, + // Initialize Lock Characteristics + this.LockMechanism.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LockMechanism.Name) + .getCharacteristic(this.hap.Characteristic.LockTargetState) + .onGet(() => { + return this.LockMechanism!.On; }) .onSet(this.OnSet.bind(this)); - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - } else if (this.botDeviceType === 'lock') { - // Initialize Switch property - this.Lock = { - Service: accessory.getService(this.hap.Service.LockMechanism) as Service, - On: accessory.context.On || false, - }; + accessory.context.Name = this.LockMechanism.Name; + + // Remove other services this.removeFanService(accessory); this.removeDoorService(accessory); this.removeOutletService(accessory); @@ -271,21 +326,26 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add lockService - const lockService = `${accessory.displayName} Lock`; - (this.Lock!.Service = accessory.getService(this.hap.Service.LockMechanism) - || accessory.addService(this.hap.Service.LockMechanism)), lockService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`); - - this.Lock!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Lock!.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'faucet') { - // Initialize Switch property + // Initialize Faucet Service this.Faucet = { - Service: accessory.getService(this.hap.Service.Faucet) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`); + + // Initialize Faucet Characteristics + this.Faucet.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Faucet.Name) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Faucet!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Faucet.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -295,21 +355,26 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add faucetService - const faucetService = `${accessory.displayName} Faucet`; - (this.Faucet!.Service = accessory.getService(this.hap.Service.Faucet) - || accessory.addService(this.hap.Service.Faucet)), faucetService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`); - - this.Faucet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Faucet!.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'fan') { - // Initialize Switch property + // Initialize Fan Service this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`); + + // Initialize Fan Characteristics + this.Fan.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Fan!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Fan.Name; + + // Remove other services this.removeLockService(accessory); this.removeDoorService(accessory); this.removeFaucetService(accessory); @@ -319,21 +384,27 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add fanService - const fanService = `${accessory.displayName} Fan`; - (this.Fan!.Service = accessory.getService(this.hap.Service.Fanv2) - || accessory.addService(this.hap.Service.Fanv2)), fanService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`); - - this.Fan!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Fan!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.botDeviceType === 'stateful') { - // Initialize Switch property + // Initialize StatefulProgrammableSwitch Service this.StatefulProgrammableSwitch = { - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); + + // Initialize StatefulProgrammableSwitch Characteristics + this.StatefulProgrammableSwitch.Service + .setCharacteristic(this.hap.Characteristic.Name, this.StatefulProgrammableSwitch.Name) + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + .onGet(() => { + return this.StatefulProgrammableSwitch!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.StatefulProgrammableSwitch.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -343,23 +414,26 @@ export class Bot extends deviceBase { this.removeWindowService(accessory); this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); - - // Add statefulProgrammableSwitchService - const statefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`; - (this.StatefulProgrammableSwitch!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) || - accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), statefulProgrammableSwitchService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); - - this.StatefulProgrammableSwitch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.StatefulProgrammableSwitch!.Service - .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) - .onSet(this.OnSet.bind(this)); } else { // Initialize Switch property this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`); + + // Initialize Outlet Characteristics + this.Outlet.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Outlet.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Outlet!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Outlet.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -369,28 +443,11 @@ export class Bot extends deviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add outletService - const outletService = `${accessory.displayName} Outlet`; - (this.Outlet!.Service = accessory.getService(this.hap.Service.Outlet) - || accessory.addService(this.hap.Service.Outlet)), outletService; - this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`); - - this.Outlet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } // Retrieve initial values and updateHomekit this.refreshStatus(); - // batteryService - const batteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; - - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -836,10 +893,10 @@ export class Bot extends deviceBase { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); if (value === this.hap.Characteristic.LockTargetState.SECURED) { await this.setOn(false); - this.Lock!.On = false; + this.LockMechanism!.On = false; } else { await this.setOn(true); - this.Lock!.On = true; + this.LockMechanism!.On = true; } } else if (this.botDeviceType === 'faucet') { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); @@ -966,23 +1023,27 @@ export class Bot extends deviceBase { await this.setOn(Boolean(this.WindowCovering!.On)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); } else if (this.botDeviceType === 'lock') { - if (this.Lock!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Lock!.On}`); + if (this.LockMechanism!.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockMechanism!.On}`); } else { - if (this.Lock!.On) { - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); + if (this.LockMechanism!.On) { + this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + this.hap.Characteristic.LockTargetState.UNSECURED); + this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.hap.Characteristic.LockCurrentState.UNSECURED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.Lock!.On})`); + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.LockMechanism!.On})`); } else { - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + this.hap.Characteristic.LockTargetState.SECURED); + this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.hap.Characteristic.LockCurrentState.SECURED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.Lock!.On})`); + + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.LockMechanism!.On})`); } } - await this.setOn(Boolean(this.Lock!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.Lock!.On}`); + await this.setOn(Boolean(this.LockMechanism!.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.LockMechanism!.On}`); } else if (this.botDeviceType === 'faucet') { if (this.Faucet!.On === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Faucet!.On}`); @@ -1097,10 +1158,10 @@ export class Bot extends deviceBase { async removeLockService(accessory: PlatformAccessory): Promise { // If lockService still present, then remove first - if (this.Lock?.Service) { - this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + if (this.LockMechanism?.Service) { + this.LockMechanism!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.Lock!.Service); + accessory.removeService(this.LockMechanism!.Service); } } @@ -1169,7 +1230,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'windowcovering') { On = this.WindowCovering!.On ? true : false; } else if (this.botDeviceType === 'lock') { - On = this.Lock!.On ? true : false; + On = this.LockMechanism!.On ? true : false; } else if (this.botDeviceType === 'faucet') { On = this.Faucet!.On ? true : false; } else if (this.botDeviceType === 'fan') { @@ -1194,7 +1255,7 @@ export class Bot extends deviceBase { } else if (this.botDeviceType === 'windowcovering') { this.WindowCovering!.On = On; } else if (this.botDeviceType === 'lock') { - this.Lock!.On = On; + this.LockMechanism!.On = On; } else if (this.botDeviceType === 'faucet') { this.Faucet!.On = On; } else if (this.botDeviceType === 'fan') { diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index dc8b7da9..feb31678 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -21,6 +21,7 @@ import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstru export class CeilingLight extends deviceBase { // Services private LightBulb: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; Hue: CharacteristicValue; @@ -52,35 +53,44 @@ export class CeilingLight extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, - On: accessory.context.On || false, - Hue: accessory.context.Hue || 0, - Saturation: accessory.context.Saturation || 0, - Brightness: accessory.context.Brightness || 0, - ColorTemperature: accessory.context.ColorTemperature || 140, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, + On: accessory.context.On ?? false, + Hue: accessory.context.Hue ?? 0, + Saturation: accessory.context.Saturation ?? 0, + Brightness: accessory.context.Brightness ?? 0, + ColorTemperature: accessory.context.ColorTemperature ?? 140, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - const LightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.LightBulb!.Service = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - - - if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { - this.accessory.removeService(this.LightBulb!.Service); - this.LightBulb!.Service = this.accessory.addService(this.hap.Service.Lightbulb); - this.accessory.context.adaptiveLighting = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); + // Adaptive Lighting + if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) { + accessory.removeService(this.LightBulb.Service); + this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb); + accessory.context.adaptiveLighting = false; + this.debugLog(`${device.deviceType}: ${accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting}`); } + if (this.adaptiveLightingShift !== -1) { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { + customTemperatureAdjustment: this.adaptiveLightingShift, + }); + this.accessory.configureController(this.AdaptiveLightingController); + this.accessory.context.adaptiveLighting = true; + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - this.LightBulb!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // handle on / off events using the On characteristic - this.LightBulb!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + // Initialize LightBulb Characteristics + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb.On; + }) + .onSet(this.OnSet.bind(this)); - // handle Brightness events using the Brightness characteristic - this.LightBulb!.Service + // Initialize LightBulb Brightness + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ minStep: this.minStep(device), @@ -93,8 +103,8 @@ export class CeilingLight extends deviceBase { }) .onSet(this.BrightnessSet.bind(this)); - // handle ColorTemperature events using the ColorTemperature characteristic - this.LightBulb!.Service + // Initialize LightBulb ColorTemperature + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ minValue: 140, @@ -106,8 +116,8 @@ export class CeilingLight extends deviceBase { }) .onSet(this.ColorTemperatureSet.bind(this)); - // handle Hue events using the Hue characteristic - this.LightBulb!.Service + // Initialize LightBulb Hue + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ minValue: 0, @@ -119,8 +129,8 @@ export class CeilingLight extends deviceBase { }) .onSet(this.HueSet.bind(this)); - // handle Hue events using the Hue characteristic - this.LightBulb!.Service + // Initialize LightBulb Saturation + this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ minValue: 0, @@ -131,19 +141,10 @@ export class CeilingLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); + accessory.context.LightBulb.Name = this.LightBulb.Name; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb!.Service, { - customTemperatureAdjustment: this.adaptiveLightingShift, - }); - this.accessory.configureController(this.AdaptiveLightingController); - this.accessory.context.adaptiveLighting = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + - ` adaptiveLightingShift: ${this.adaptiveLightingShift}`, - ); - } + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 3699afa8..72fb3d03 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -21,6 +21,7 @@ import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstru export class ColorBulb extends deviceBase { // Services private LightBulb: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; Hue: CharacteristicValue; @@ -53,36 +54,42 @@ export class ColorBulb extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, - On: accessory.context.On || false, - Hue: accessory.context.Hue || 0, - Saturation: accessory.context.Saturation || 0, - Brightness: accessory.context.Brightness || 0, - ColorTemperature: accessory.context.ColorTemperature || 140, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, + On: accessory.context.On ?? false, + Hue: accessory.context.Hue ?? 0, + Saturation: accessory.context.Saturation ?? 0, + Brightness: accessory.context.Brightness ?? 0, + ColorTemperature: accessory.context.ColorTemperature ?? 140, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the Lightbulb service if it exists, otherwise create a new Lightbulb service - // you can create multiple services for each accessory - const lightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService; - - if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { - this.accessory.removeService(this.LightBulb.Service); - this.LightBulb.Service = this.accessory.addService(this.hap.Service.Lightbulb); - this.accessory.context.adaptiveLighting = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); + // Adaptive Lighting + if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) { + accessory.removeService(this.LightBulb.Service); + this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb); + accessory.context.adaptiveLighting = false; + this.debugLog(`${device.deviceType}: ${accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting}`); } + if (this.adaptiveLightingShift !== -1) { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { + customTemperatureAdjustment: this.adaptiveLightingShift, + }); + accessory.configureController(this.AdaptiveLightingController); + accessory.context.adaptiveLighting = true; + this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting},` + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); + } + this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // handle on / off events using the On characteristic - this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + // Initialize LightBulb Characteristics + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb.On; + }) + .onSet(this.OnSet.bind(this)); - // handle Brightness events using the Brightness characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ @@ -96,7 +103,6 @@ export class ColorBulb extends deviceBase { }) .onSet(this.BrightnessSet.bind(this)); - // handle ColorTemperature events using the ColorTemperature characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ @@ -109,7 +115,6 @@ export class ColorBulb extends deviceBase { }) .onSet(this.ColorTemperatureSet.bind(this)); - // handle Hue events using the Hue characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ @@ -122,7 +127,6 @@ export class ColorBulb extends deviceBase { }) .onSet(this.HueSet.bind(this)); - // handle Hue events using the Hue characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ @@ -134,19 +138,10 @@ export class ColorBulb extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); + accessory.context.LightBulb.Name = this.LightBulb.Name; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { - customTemperatureAdjustment: this.adaptiveLightingShift, - }); - this.accessory.configureController(this.AdaptiveLightingController); - this.accessory.context.adaptiveLighting = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + - ` adaptiveLightingShift: ${this.adaptiveLightingShift}`, - ); - } + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/contact.ts b/src/device/contact.ts index 634202e5..a80eeb16 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -9,7 +9,7 @@ import { skipWhile } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -19,22 +19,26 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Contact extends deviceBase { // Services private ContactSensor: { + Name: CharacteristicValue; Service: Service; ContactSensorState: CharacteristicValue; }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private MotionSensor?: { + Name: CharacteristicValue; Service: Service; MotionDetected: CharacteristicValue; }; private LightSensor?: { + Name: CharacteristicValue; Service: Service; CurrentAmbientLightLevel: CharacteristicValue; }; @@ -55,84 +59,94 @@ export class Contact extends deviceBase { // Initialize Contact Sensor property this.ContactSensor = { - Service: accessory.getService(this.hap.Service.ContactSensor) as Service, - ContactSensorState: this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, + Name: accessory.context.ContactSensor.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.ContactSensor) ?? accessory.addService(this.hap.Service.ContactSensor) as Service, + ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; + // Initialize ContactSensor Characteristics + this.ContactSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.ContactSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.ContactSensorState) + .onGet(() => { + return this.ContactSensor.ContactSensorState; + }); + accessory.context.ContactSensor.Name = this.ContactSensor.Name; + // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: 100, - StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; - // Initialize Motion Sensor property - if (!this.device.contact?.hide_motionsensor) { - this.MotionSensor = { - Service: accessory.getService(this.hap.Service.MotionSensor) as Service, - MotionDetected: false, - }; - } - - // Initialize Light Sensor property - if (!this.device.contact?.hide_lightsensor) { - this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: 0, - }; - } - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the Contact service if it exists, otherwise create a new Contact service - // you can create multiple services for each accessory - const ContactSensorService = `${accessory.displayName} Contact Sensor`; - (this.ContactSensor.Service = accessory.getService(this.hap.Service.ContactSensor) - || accessory.addService(this.hap.Service.ContactSensor)), ContactSensorService; + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); - this.ContactSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery) + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Motion Sensor Service - if (device.contact?.hide_motionsensor) { + // Initialize Motion Sensor Service + if (this.device.contact?.hide_motionsensor) { this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; accessory.removeService(this.MotionSensor!.Service); - } else if (!this.MotionSensor?.Service && !device.contact?.hide_motionsensor) { - this.debugLog(`${device.deviceType}: ${accessory.displayName} Add Motion Sensor Service`); - const MotionService = `${accessory.displayName} Motion Sensor`; - (this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) - || accessory.addService(this.hap.Service.MotionSensor)), MotionService; - - this.MotionSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, MotionService); } else { - this.debugLog(`${device.deviceType}: ${accessory.displayName} Motion Sensor Service Not Added`); + this.MotionSensor = { + Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, + Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, + MotionDetected: accessory.context.MotionDetected ?? false, + }; + + // Motion Sensor Characteristics + this.MotionSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.MotionSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.MotionDetected) + .onGet(() => { + return this.MotionSensor!.MotionDetected; + }); + accessory.context.MotionSensor.Name = this.MotionSensor.Name; } - // Light Sensor Service + // Initialize Light Sensor Service if (device.contact?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor?.Service && !device.contact?.hide_lightsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - - const LightSensorService = `${accessory.displayName} Light Sensor`; - (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - - this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); - } + this.LightSensor = { + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, + }; - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; + // Light Sensor Characteristics + this.LightSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel) + .onGet(() => { + return this.LightSensor!.CurrentAmbientLightLevel; + }); + accessory.context.LightSensor.Name = this.LightSensor.Name; + } - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -152,8 +166,8 @@ export class Contact extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); const { detectionState, brightness, openState } = context; const { ContactSensorState } = this.ContactSensor; - const { CurrentAmbientLightLevel } = this.LightSensor || {}; - const { MotionDetected } = this.MotionSensor || {}; + const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; + const { MotionDetected } = this.MotionSensor ?? {}; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + '(detectionState, brightness, openState) = ' + `Webhook:(${detectionState}, ${brightness}, ${openState}), ` + @@ -482,11 +496,14 @@ export class Contact extends deviceBase { async apiError(e: any): Promise { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e); + this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); if (!this.device.contact?.hide_motionsensor) { this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, e); + this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); } if (!this.device.contact?.hide_lightsensor) { this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 0349fd85..b41dd96c 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -16,6 +16,7 @@ import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicCha export class Curtain extends deviceBase { // Services private WindowCovering: { + Name: CharacteristicValue; Service: Service; PositionState: CharacteristicValue; TargetPosition: CharacteristicValue; @@ -24,12 +25,15 @@ export class Curtain extends deviceBase { }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; + ChargingState: CharacteristicValue; }; private LightSensor?: { + Name: CharacteristicValue; Service: Service; CurrentAmbientLightLevel?: CharacteristicValue; }; @@ -57,49 +61,29 @@ export class Curtain extends deviceBase { // Initialize LightBulb property this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering) as Service, - PositionState: accessory.context.PositionState || this.hap.Characteristic.PositionState.STOPPED, - TargetPosition: accessory.context.TargetPosition || 100, - CurrentPosition: accessory.context.CurrentPosition || 100, - HoldPosition: accessory.context.HoldPosition || false, + Name: accessory.context.WindowCovering.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, + PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, + TargetPosition: accessory.context.TargetPosition ?? 100, + CurrentPosition: accessory.context.CurrentPosition ?? 100, + HoldPosition: accessory.context.HoldPosition ?? false, }; - // Initialize Battery property - this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - }; - - // Initialize LightSensor property - if (!this.device.curtain?.hide_lightsensor) { - this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, - }; - } - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the WindowCovering service if it exists, otherwise create a new WindowCovering service - // you can create multiple services for each accessory - const WindowCoveringService = `${accessory.displayName} ${device.deviceType}`; - (this.WindowCovering.Service = accessory.getService(this.hap.Service.WindowCovering) - || accessory.addService(this.hap.Service.WindowCovering)), WindowCoveringService; - - this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/WindowCovering - - // create handlers for required characteristics - this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); + // Initialize WindowCovering Service + this.WindowCovering.Service. + setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name) + .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false) + .getCharacteristic(this.hap.Characteristic.PositionState) + .onGet(() => { + return this.WindowCovering.PositionState; + }); + accessory.context.WindowCovering.Name = this.WindowCovering.Name; + // Initialize WindowCovering CurrentPosition this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ - minStep: this.minStep(device), + minStep: Number(this.minStep(device)), minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -108,42 +92,83 @@ export class Curtain extends deviceBase { return this.WindowCovering.CurrentPosition; }); + // Initialize WindowCovering TargetPosition this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ - minStep: this.minStep(device), + minStep: Number(this.minStep(device)), minValue: 0, maxValue: 100, validValueRanges: [0, 100], }) + .onGet(() => { + return this.WindowCovering.TargetPosition; + }) .onSet(this.TargetPositionSet.bind(this)); + // Initialize WindowCovering TargetPosition this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.HoldPosition) + .onGet(() => { + return this.WindowCovering.HoldPosition; + }) .onSet(this.HoldPositionSet.bind(this)); - // Light Sensor Service + // Initialize Battery property + this.Battery = { + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, + }; + + // Initialize Battery Service + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + accessory.context.Battery.Name = this.Battery.Name; + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.ChargingState) + .onGet(() => { + return this.Battery.ChargingState; + }); + + // Initialize LightSensor Service if (device.curtain?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const LightSensorService = `${accessory.displayName} Light Sensor`; - (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - - this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); - } + this.LightSensor = { + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, + }; - // Battery Service - const batteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), batteryService; + // Initialize LightSensor Characteristic + this.LightSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel) + .onGet(() => { + return this.LightSensor!.CurrentAmbientLightLevel!; + }); + accessory.context.LightSensor.Name = this.LightSensor.Name; + } - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); @@ -345,30 +370,37 @@ export class Curtain extends deviceBase { case 3: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 4: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 5: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 6: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 7: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 8: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 9: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`); + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; break; case 10: default: @@ -441,12 +473,13 @@ export class Curtain extends deviceBase { const set_minLux = await this.minLux(); const set_maxLux = await this.maxLux(); switch (deviceStatus.body.brightness) { - case 'dim': - this.LightSensor!.CurrentAmbientLightLevel = set_minLux; - break; case 'bright': - default: this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; + this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING; + break; + case 'dim': + default: + this.LightSensor!.CurrentAmbientLightLevel = set_minLux; } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); @@ -843,6 +876,17 @@ export class Curtain extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } + if (this.Battery.ChargingState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`); + } else { + if (this.device.mqttURL) { + this.mqttPublish('ChargingState', this.Battery.ChargingState.toString()); + } + this.accessory.context.ChargingState = this.Battery.ChargingState; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ChargingState: ${this.Battery.ChargingState}`); + } } async BLEPushConnection() { @@ -908,7 +952,7 @@ export class Curtain extends deviceBase { } } - minStep(device: device & devicesConfig): number { + async minStep(device: device & devicesConfig): Promise { let set_minStep: number; if (device.curtain?.set_minStep) { set_minStep = device.curtain?.set_minStep; @@ -950,11 +994,12 @@ export class Curtain extends deviceBase { this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e); if (!this.device.curtain?.hide_lightsensor) { this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); + this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); } - this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); - this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); - //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE); } } diff --git a/src/device/device.ts b/src/device/device.ts index 082dfa72..2613076b 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -565,15 +565,13 @@ export abstract class deviceBase { } async statusCode(statusCode: number): Promise { - switch (statusCode) { - case 171: - switch (this.device.deviceId) { - case this.device.hubDeviceId: - case '000000000000': - statusCode = 161; - break; - } - break; + if (statusCode === 171) { + if (this.device.deviceId === this.device.hubDeviceId) { + statusCode = 161; + } + if (this.device.deviceId === '000000000000') { + statusCode = 161; + } } switch (statusCode) { case 151: diff --git a/src/device/fan.ts b/src/device/fan.ts index 8a87fab0..3a51da38 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -14,6 +14,7 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Fan extends deviceBase { // Services private Fan: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; SwingMode: CharacteristicValue; @@ -21,6 +22,7 @@ export class Fan extends deviceBase { }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; @@ -41,42 +43,75 @@ export class Fan extends deviceBase { this.doPlugUpdate = new Subject(); this.plugUpdateInProgress = false; - // Initialize Fan property + // Initialize Fan Service this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, - SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, - RotationSpeed: accessory.context.RotationSpeed || 0, + Name: accessory.context.Fan.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, + SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED, + RotationSpeed: accessory.context.RotationSpeed ?? 0, }; + // Initialize Fan Service + this.Fan.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Fan.Active; + }) + .onSet(this.ActiveSet.bind(this)); + + // Initialize Fan RotationSpeed Characteristic + this.Fan.Service + .getCharacteristic(this.hap.Characteristic.RotationSpeed) + .onGet(() => { + return this.Fan.RotationSpeed; + }) + .onSet(this.RotationSpeedSet.bind(this)); + + // Initialize Fan SwingMode Characteristic + this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode) + .onGet(() => { + return this.Fan.SwingMode; + }) + .onSet(this.SwingModeSet.bind(this)); + accessory.context.Fan.Name = this.Fan.Name; + // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGING, + Name: accessory.context.Battery.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, }; + // Initialize Battery Service + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + + // Initialize Battery ChargingState Characteristic + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.ChargingState) + .onGet(() => { + return this.Battery.ChargingState; + }); + + // Initialize Battery StatusLowBattery Characteristic + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; + // Retrieve initial values and updateHomekit this.refreshStatus(); - // get the Fan service if it exists, otherwise create a new Fanv2 service - // you can create multiple services for each accessory - const FanService = `${accessory.displayName} ${device.deviceType}`; - (this.Fan.Service = accessory.getService(this.hap.Service.Fanv2) - || accessory.addService(this.hap.Service.Fanv2)), FanService; - - this.Fan.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/Fanv2 - - // create handlers for required characteristics - this.Fan.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - // create handlers for required characteristics - this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed).onSet(this.RotationSpeedSet.bind(this)); - // create handlers for required characteristics - this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode).onSet(this.SwingModeSet.bind(this)); - // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/hub.ts b/src/device/hub.ts index ae95cde3..6d2d8baa 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -10,21 +10,24 @@ import { Subject, interval, skipWhile } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; import type { device, deviceStatus, devicesConfig } from '../settings.js'; -import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; export class Hub extends deviceBase { // Services private LightSensor?: { + Name: CharacteristicValue; Service: Service; CurrentAmbientLightLevel: CharacteristicValue; }; private HumiditySensor?: { + Name: CharacteristicValue; Service: Service; CurrentRelativeHumidity: CharacteristicValue; }; private TemperatureSensor?: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -43,46 +46,21 @@ export class Hub extends deviceBase { this.doHubUpdate = new Subject(); this.hubUpdateInProgress = false; - // Initialize Temperature Sensor property - if (!device.hub?.hide_temperature) { - this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, - CurrentTemperature: accessory.context.CurrentTemperature || 0, - }; - } - - // Initialize Humidity Sensor property - if (!device.hub?.hide_humidity) { - this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, - CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 0, - }; - } - - // Initialize Light Sensor property - if (!device.hub?.hide_lightsensor) { - this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0.0001, - }; - } - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // Temperature Sensor Service + // Initialize Temperature Sensor Service if (device.hub?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; - (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - - this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); - this.TemperatureSensor!.Service + } else { + this.TemperatureSensor = { + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, + CurrentTemperature: accessory.context.CurrentTemperature ?? 0, + }; + + // Initialize Temperature Sensor Characteristic + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -94,23 +72,24 @@ export class Hub extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } - // Humidity Sensor Service + // Initialize Humidity Sensor Service if (device.hub?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; - (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; + } else { + this.HumiditySensor = { + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 0, + }; - this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, @@ -118,26 +97,37 @@ export class Hub extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); + accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; } - // Light Sensor Service + // Initialize Light Sensor Service if (device.hub?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const LightSensorService = `${accessory.displayName} Light Sensor`; - (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - - this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); + this.LightSensor = { + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, + }; + + // Initialize Light Sensor Characteristics + this.LightSensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name) + .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel) + .setProps({ + minStep: 1, + }) + .onGet(() => { + return this.LightSensor!.CurrentAmbientLightLevel; + }); + accessory.context.LightSensor.Name = this.LightSensor.Name; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Retrieve initial values and update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 6f2c14d2..3cfb35c6 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -7,7 +7,7 @@ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -17,6 +17,7 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Humidifier extends deviceBase { // Services private HumidifierDehumidifier: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; WaterLevel: CharacteristicValue; @@ -27,6 +28,7 @@ export class Humidifier extends deviceBase { }; private TemperatureSensor?: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -47,46 +49,24 @@ export class Humidifier extends deviceBase { // Initialize the HumidifierDehumidifier Service this.HumidifierDehumidifier = { - Service: accessory.getService(this.hap.Service.HumidifierDehumidifier) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.ACTIVE, - WaterLevel: accessory.context.WaterLevel || 100, - CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, + Name: accessory.context.HumidifierDehumidifier.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.HumidifierDehumidifier) + ?? accessory.addService(this.hap.Service.HumidifierDehumidifier) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.ACTIVE, + WaterLevel: accessory.context.WaterLevel ?? 100, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, TargetHumidifierDehumidifierState: accessory.context.TargetHumidifierDehumidifierState - || this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER, + ?? this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER, CurrentHumidifierDehumidifierState: accessory.context.CurrentHumidifierDehumidifierState - || this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE, - RelativeHumidityHumidifierThreshold: accessory.context.RelativeHumidityHumidifierThreshold || 50, + ?? this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE, + RelativeHumidityHumidifierThreshold: accessory.context.RelativeHumidityHumidifierThreshold ?? 50, }; - // Initialize the Temperature Sensor Service - if (!device.humidifier?.hide_temperature) { - this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, - CurrentTemperature: accessory.context.CurrentTemperature || 30, - }; - } - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the service if it exists, otherwise create a new service - // you can create multiple services for each accessory - const HumidifierDehumidifierService = `${accessory.displayName} Humidifier`; - (this.HumidifierDehumidifier.Service = accessory.getService(this.hap.Service.HumidifierDehumidifier) - || accessory.addService(this.hap.Service.HumidifierDehumidifier)), HumidifierDehumidifierService; - - this.HumidifierDehumidifier.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/HumidifierDehumidifier - - // create handlers for required characteristics - this.HumidifierDehumidifier.Service.setCharacteristic( - this.hap.Characteristic.CurrentHumidifierDehumidifierState, - this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState, - ); - + // Initialize the HumidifierDehumidifier Characteristics this.HumidifierDehumidifier.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HumidifierDehumidifier.Name) + .setCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, + this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState) .getCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState) .setProps({ validValueRanges: [0, 1], @@ -94,9 +74,17 @@ export class Humidifier extends deviceBase { maxValue: 1, validValues: [0, 1], }) + .onGet(() => { + return this.HumidifierDehumidifier.TargetHumidifierDehumidifierState; + }) .onSet(this.TargetHumidifierDehumidifierStateSet.bind(this)); - this.HumidifierDehumidifier.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.HumidifierDehumidifier.Service + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.HumidifierDehumidifier.Active; + }) + .onSet(this.ActiveSet.bind(this)); this.HumidifierDehumidifier.Service .getCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold) @@ -106,21 +94,27 @@ export class Humidifier extends deviceBase { maxValue: 100, minStep: this.minStep(device), }) + .onGet(() => { + return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; + }) .onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this)); + accessory.context.HumidifierDehumidifier.Name = this.HumidifierDehumidifier.Name; - // Temperature Sensor Service - if (device.humidifier?.hide_temperature || this.BLE) { + // Initialize the Temperature Sensor Service + if (device.humidifier?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor?.Service && !this.BLE) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; - (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - - this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); - this.TemperatureSensor!.Service + } else { + this.TemperatureSensor = { + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, + CurrentTemperature: accessory.context.CurrentTemperature || 30, + }; + + // Initialize the Temperature Sensor Characteristics + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ validValueRanges: [-273.15, 100], @@ -131,10 +125,12 @@ export class Humidifier extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } + // Retrieve initial values and updateHomekit + this.refreshStatus(); + // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -155,9 +151,9 @@ export class Humidifier extends deviceBase { const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + '(temperature, humidity) = ' + + `Webhook:(${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; if (!this.device.humidifier?.hide_temperature) { this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 71c25145..84ab6098 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -9,8 +9,8 @@ import { convertUnits } from '../utils.js'; import { Subject, interval, skipWhile } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; -import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -20,17 +20,20 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class IOSensor extends deviceBase { // Services private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private HumiditySensor?: { + Name: CharacteristicValue; Service: Service; CurrentRelativeHumidity: CharacteristicValue; }; private TemperatureSensor?: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -49,45 +52,45 @@ export class IOSensor extends deviceBase { this.doIOSensorUpdate = new Subject(); this.ioSensorUpdateInProgress = false; - // Initialize Temperature Sensor property - if (!device.iosensor?.hide_temperature) { - this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, - CurrentTemperature: accessory.context.CurrentTemperature || 30, - }; - } - - // Initialize Humidity Sensor property - if (!device.iosensor?.hide_humidity) { - this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, - CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, - }; - } - // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Temperature Sensor Service + // InitializeTemperature Sensor Service if (device.iosensor?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; - (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - - this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); - this.TemperatureSensor!.Service + } else { + this.TemperatureSensor = { + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, + CurrentTemperature: accessory.context.CurrentTemperature ?? 30, + }; + + // Initialize Temperature Sensor Characteristics + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -97,25 +100,26 @@ export class IOSensor extends deviceBase { minStep: 0.1, }) .onGet(() => { - return this.TemperatureSensor!.CurrentTemperature!; + return this.TemperatureSensor!.CurrentTemperature; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } - // Humidity Sensor Service + // Initialize Humidity Sensor Service if (device.iosensor?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; - (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; - - this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); - this.HumiditySensor!.Service + } else { + this.HumiditySensor = { + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, + }; + + // Initialize Humidity Sensor Characteristics + this.HumiditySensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, @@ -123,17 +127,11 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); + accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; } - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; - - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index e2deb451..e3cb8365 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -17,6 +17,7 @@ import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstru export class StripLight extends deviceBase { // Services private LightBulb: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; Hue: CharacteristicValue; @@ -44,37 +45,47 @@ export class StripLight extends deviceBase { this.doStripLightUpdate = new Subject(); this.stripLightUpdateInProgress = false; - // Initialize the LightBulb property + // Initialize the LightBulb Service this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, - On: accessory.context.On || false, - Hue: accessory.context.Hue || 0, - Saturation: accessory.context.Saturation || 0, - Brightness: accessory.context.Brightness || 0, - ColorTemperature: accessory.context.ColorTemperature || 140, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, + On: accessory.context.On ?? false, + Hue: accessory.context.Hue ?? 0, + Saturation: accessory.context.Saturation ?? 0, + Brightness: accessory.context.Brightness ?? 0, + ColorTemperature: accessory.context.ColorTemperature ?? 140, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the Lightbulb service if it exists, otherwise create a new Lightbulb service - // you can create multiple services for each accessory - const LightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - + // Adaptive Lighting if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { this.accessory.removeService(this.LightBulb.Service); this.LightBulb.Service = this.accessory.addService(this.hap.Service.Lightbulb); this.accessory.context.adaptiveLighting = false; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`); } + if (this.adaptiveLightingShift !== -1) { + this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { + customTemperatureAdjustment: this.adaptiveLightingShift, + }); + this.accessory.configureController(this.AdaptiveLightingController); + this.accessory.context.adaptiveLighting = true; + this.debugLog( + `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`, + ); + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // handle on / off events using the On characteristic - this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + // Initialize LightBulb Characteristics + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb.On; + }) + .onSet(this.OnSet.bind(this)); - // handle Brightness events using the Brightness characteristic + // Initialize LightBulb Brightness Characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ @@ -88,7 +99,7 @@ export class StripLight extends deviceBase { }) .onSet(this.BrightnessSet.bind(this)); - // handle ColorTemperature events using the ColorTemperature characteristic + // Initialize LightBulb ColorTemperature Characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.ColorTemperature) .setProps({ @@ -101,7 +112,7 @@ export class StripLight extends deviceBase { }) .onSet(this.ColorTemperatureSet.bind(this)); - // handle Hue events using the Hue characteristic + // Initialize LightBulb Hue Characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Hue) .setProps({ @@ -114,7 +125,7 @@ export class StripLight extends deviceBase { }) .onSet(this.HueSet.bind(this)); - // handle Hue events using the Hue characteristic + // Initialize LightBulb Saturation Characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Saturation) .setProps({ @@ -126,19 +137,10 @@ export class StripLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); + accessory.context.LightBulb.Name = this.LightBulb.Name; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); - if (this.adaptiveLightingShift !== -1) { - this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, { - customTemperatureAdjustment: this.adaptiveLightingShift, - }); - this.accessory.configureController(this.AdaptiveLightingController); - this.accessory.context.adaptiveLighting = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + - ` adaptiveLightingShift: ${this.adaptiveLightingShift}`, - ); - } + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/lock.ts b/src/device/lock.ts index 4f75b978..b5a891a3 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -11,23 +11,27 @@ import type { device, devicesConfig, deviceStatus, serviceData } from '../settin export class Lock extends deviceBase { // Services private LockMechanism: { + Name: CharacteristicValue; Service: Service; LockTargetState: CharacteristicValue; LockCurrentState: CharacteristicValue; }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private ContactSensor?: { + Name: CharacteristicValue; Service: Service; ContactSensorState: CharacteristicValue; }; private Switch?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -46,91 +50,96 @@ export class Lock extends deviceBase { this.doLockUpdate = new Subject(); this.lockUpdateInProgress = false; - // Initialize LockMechanism property + // Initialize LockMechanism Service this.LockMechanism = { - Service: accessory.getService(this.hap.Service.LockMechanism) as Service, - LockTargetState: accessory.context.LockTargetState || this.hap.Characteristic.LockTargetState.SECURED, - LockCurrentState: accessory.context.LockCurrentState || this.hap.Characteristic.LockCurrentState.SECURED, + Name: accessory.context.LockMechanism.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, + LockTargetState: accessory.context.LockTargetState ?? this.hap.Characteristic.LockTargetState.SECURED, + LockCurrentState: accessory.context.LockCurrentState ?? this.hap.Characteristic.LockCurrentState.SECURED, }; + // Initialize LockMechanism Characteristics + this.LockMechanism.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LockMechanism.Name) + .getCharacteristic(this.hap.Characteristic.LockTargetState) + .onGet(() => { + return this.LockMechanism.LockTargetState; + }) + .onSet(this.LockTargetStateSet.bind(this)); + accessory.context.LockMechanism.Name = this.LockMechanism.Name; + // Initialize Battery property this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; - // Initialize ContactSensor property - if (!this.device.lock?.hide_contactsensor) { + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; + + // Contact Sensor Service + if (device.lock?.hide_contactsensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); + this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; + accessory.removeService(this.ContactSensor!.Service); + } else { this.ContactSensor = { - Service: accessory.getService(this.hap.Service.ContactSensor) as Service, - ContactSensorState: accessory.context.ContactSensorState || this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, + Name: accessory.context.ContactSensor.Name ?? `${accessory.displayName} Contact Sensor`, + Service: accessory.getService(this.hap.Service.ContactSensor) ?? this.accessory.addService(this.hap.Service.ContactSensor) as Service, + ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; - } - // Initialize Latch Button Service - if (device.lock?.activate_latchbutton) { - this.Switch = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - On: accessory.context.On || false, - }; + // Initialize Contact Sensor Characteristics + this.ContactSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.ContactSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.ContactSensorState) + .onGet(() => { + return this.ContactSensor!.ContactSensorState; + }); + accessory.context.ContactSensor.Name = this.ContactSensor.Name; } - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the LockMechanism service if it exists, otherwise create a new LockMechanism service - // you can create multiple services for each accessory - const LockMechanismService = `${accessory.displayName} ${device.deviceType}`; - (this.LockMechanism.Service = accessory.getService(this.hap.Service.LockMechanism) - || accessory.addService(this.hap.Service.LockMechanism)), LockMechanismService; - - this.LockMechanism.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - this.LockMechanism.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.LockTargetStateSet.bind(this)); - - // Latch Button Service + // Initialize Latch Button Service if (device.lock?.activate_latchbutton === false) { // remove the service when this variable is false this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Latch Button Service`); this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service; accessory.removeService(this.Switch!.Service); - } else if (!this.Switch!.Service && device.lock?.activate_latchbutton === true) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Adding Latch Button Service`); - const latchServiceName = `${accessory.displayName} Latch`; - this.Switch!.Service = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch, latchServiceName, 'Switch.ServiceIdentifier'); - - this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, latchServiceName); - - this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Latch Button Service Not Added`); - } - - - // Contact Sensor Service - if (device.lock?.hide_contactsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); - this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; - accessory.removeService(this.ContactSensor!.Service); - } else if (!this.ContactSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Contact Sensor Service`); - const ContactSensorService = `${accessory.displayName} Contact Sensor`; - (this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) - || this.accessory.addService(this.hap.Service.ContactSensor)), ContactSensorService; + this.Switch = { + Name: accessory.context.Switch.Name ?? `${accessory.displayName} Latch`, + Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, + On: accessory.context.On ?? false, + }; - this.ContactSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, ContactSensorService); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Contact Sensor Service Not Added`); + // Initialize Latch Button Characteristics + this.Switch.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Switch!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Switch.Name = this.Switch.Name; } - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; - - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/meter.ts b/src/device/meter.ts index 48b30a01..f57d5fe1 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -15,17 +15,20 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Meter extends deviceBase { // Services private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private HumiditySensor?: { + Name: CharacteristicValue; Service: Service; CurrentRelativeHumidity: CharacteristicValue; }; private TemperatureSensor?: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -44,45 +47,45 @@ export class Meter extends deviceBase { this.doMeterUpdate = new Subject(); this.meterUpdateInProgress = false; - // Initialize Temperature Sensor property - if (!device.meter?.hide_temperature) { - this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, - CurrentTemperature: accessory.context.CurrentTemperature || 30, - }; - } - - // Initialize Humidity Sensor property - if (!device.meter?.hide_humidity) { - this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, - CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, - }; - } - - // Initialize Battery property + // Initialize Battery Service this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Temperature Sensor Service + // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; - (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - - this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); - this.TemperatureSensor!.Service + } else { + this.TemperatureSensor = { + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, + CurrentTemperature: accessory.context.CurrentTemperature ?? 30, + }; + + // Initialize Temperature Sensor Characteristics + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -94,23 +97,23 @@ export class Meter extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } - - // Humidity Sensor Service + // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; - (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; + } else { + this.HumiditySensor = { + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, + }; - this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, @@ -118,17 +121,11 @@ export class Meter extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); + accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; } - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; - - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 579e73f1..2b61aa56 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -19,17 +19,20 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class MeterPlus extends deviceBase { // Services private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private HumiditySensor?: { + Name: CharacteristicValue; Service: Service; CurrentRelativeHumidity: CharacteristicValue; }; private TemperatureSensor?: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -48,45 +51,45 @@ export class MeterPlus extends deviceBase { this.doMeterUpdate = new Subject(); this.meterUpdateInProgress = false; - // Initialize Temperature Sensor property - if (!device.meter?.hide_temperature) { - this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, - CurrentTemperature: accessory.context.CurrentTemperature || 30, - }; - } - - // Initialize Humidity Sensor property - if (!device.meter?.hide_humidity) { - this.HumiditySensor = { - Service: accessory.getService(this.hap.Service.HumiditySensor) as Service, - CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity || 50, - }; - } - - // Initialize Battery property + // Initialize Battery Service this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; - // Retrieve initial values and updateHomekit - this.refreshStatus(); + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); + + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Temperature Sensor Service + // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); - } else if (!this.TemperatureSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`); - const TemperatureSensorService = `${accessory.displayName} Temperature Sensor`; - (this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) - || this.accessory.addService(this.hap.Service.TemperatureSensor)), TemperatureSensorService; - - this.TemperatureSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, TemperatureSensorService); - this.TemperatureSensor!.Service + } else { + this.TemperatureSensor = { + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, + CurrentTemperature: accessory.context.CurrentTemperature ?? 30, + }; + + // Initialize Temperature Sensor Characteristics + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentTemperature) .setProps({ unit: Units['CELSIUS'], @@ -98,23 +101,23 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } - - // Humidity Sensor Service + // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); - } else if (!this.HumiditySensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`); - const HumiditySensorService = `${accessory.displayName} Humidity Sensor`; - (this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) - || this.accessory.addService(this.hap.Service.HumiditySensor)), HumiditySensorService; + } else { + this.HumiditySensor = { + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, + CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, + }; - this.HumiditySensor!.Service.setCharacteristic(this.hap.Characteristic.Name, HumiditySensorService); + // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name) .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) .setProps({ minStep: 0.1, @@ -122,17 +125,11 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`); + accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; } - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; - - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); diff --git a/src/device/motion.ts b/src/device/motion.ts index 8fc9b2c1..c04e4f88 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -19,17 +19,20 @@ import type { device, devicesConfig, serviceData, deviceStatus } from '../settin export class Motion extends deviceBase { // Services private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; }; private MotionSensor: { + Name: CharacteristicValue; Service: Service; MotionDetected: CharacteristicValue; }; private LightSensor?: { + Name: CharacteristicValue; Service: Service; CurrentAmbientLightLevel: CharacteristicValue; }; @@ -50,64 +53,72 @@ export class Motion extends deviceBase { // Initialize Motion Sensor property this.MotionSensor = { + Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, - MotionDetected: accessory.context.MotionDetected || false, + MotionDetected: accessory.context.MotionDetected ?? false, }; - // Initialize Battery property + // Initialize Motion Sensor Characteristics + this.MotionSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.MotionDetected) + .onGet(() => { + return this.MotionSensor.MotionDetected; + }); + accessory.context.MotionSensor.Name = this.MotionSensor.Name; + + // Initialize Battery Service this.Battery = { + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - }; - - // Initialize Motion Sensor property - if (!device.motion?.hide_lightsensor) { - this.LightSensor = { - Service: accessory.getService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel || 0, - }; + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - /*(this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) - || accessory.addService(this.hap.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`;*/ - - if (this.MotionSensor.Service) { - this.MotionSensor.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - } - - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/MotionSensor + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery) + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Light Sensor Service + // Initialize Light Sensor Service if (device.motion?.hide_lightsensor) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); - } else if (!this.LightSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`); - const LightSensorService = `${accessory.displayName} Light Sensor`; - (this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) - || this.accessory.addService(this.hap.Service.LightSensor)), LightSensorService; - - this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LightSensorService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`); - } + this.LightSensor = { + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0, + }; - // Battery Service - /*const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService;*/ + // Initialize LightSensor Characteristics + this.LightSensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor!.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel) + .onGet(() => { + return this.LightSensor!.CurrentAmbientLightLevel; + }); + accessory.context.LightSensor.Name = this.LightSensor.Name; + }; - if (this.Battery.Service) { - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - } + + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); diff --git a/src/device/plug.ts b/src/device/plug.ts index 7b74e005..ca3c9dcd 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -13,6 +13,7 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class Plug extends deviceBase { // Services private Outlet: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -31,28 +32,26 @@ export class Plug extends deviceBase { this.doPlugUpdate = new Subject(); this.plugUpdateInProgress = false; - // Initialize Outlet property + // Initialize Outlet Service this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet) as Service, + Name: accessory.context.Outlet.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; + // Initialize Outlet Characteristics + this.Outlet.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Outlet.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Outlet.Name = this.Outlet.Name; + // Retrieve initial values and updateHomekit this.refreshStatus(); - // get the Outlet service if it exists, otherwise create a new Outlet service - // you can create multiple services for each accessory - const OutletService = `${accessory.displayName} ${device.deviceType}`; - (this.Outlet.Service = accessory.getService(this.hap.Service.Outlet) - || accessory.addService(this.hap.Service.Outlet)), OutletService; - - this.Outlet.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/Outlet - - // create handlers for required characteristics - this.Outlet.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); - // Update Homekit this.updateHomeKitCharacteristics(); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index b8234e90..87d79c3d 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -10,20 +10,23 @@ import { debounceTime, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; +import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js'; export class RobotVacuumCleaner extends deviceBase { // Services private LightBulb: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; Brightness: CharacteristicValue; }; private Battery: { + Name: CharacteristicValue; Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; + ChargingState: CharacteristicValue; }; // Updates @@ -40,37 +43,24 @@ export class RobotVacuumCleaner extends deviceBase { this.doRobotVacuumCleanerUpdate = new Subject(); this.robotVacuumCleanerUpdateInProgress = false; - // Initialize Lightbulb property + // Initialize Lightbulb Service this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, - On: accessory.context.On || false, - Brightness: accessory.context.Brightness || 0, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, + On: accessory.context.On ?? false, + Brightness: accessory.context.Brightness ?? 0, }; - // Initialize Battery property - this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - }; - - // Retrieve initial values and updateHomekit - this.refreshStatus(); - - // get the Lightbulb service if it exists, otherwise create a new Lightbulb service - // you can create multiple services for each accessory - const LightBulbService = `${accessory.displayName} ${device.deviceType}`; - (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - - this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/Lightbulb - - // create handlers for required characteristics - this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + // Initialize LightBulb Characteristics + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb.On; + }) + .onSet(this.OnSet.bind(this)); - // handle Brightness events using the Brightness characteristic + // Initialize LightBulb Brightness Characteristic this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ @@ -84,14 +74,40 @@ export class RobotVacuumCleaner extends deviceBase { return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); + accessory.context.LightBulb.Name = this.LightBulb.Name; - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; + // Initialize Battery Service + this.Battery = { + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, + }; + + // Initialize Battery Characteristics + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.BatteryLevel; + }); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.ChargingState) + .onGet(() => { + return this.Battery.ChargingState; + }); + accessory.context.Battery.Name = this.Battery.Name; + + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Update Homekit this.updateHomeKitCharacteristics(); @@ -108,14 +124,16 @@ export class RobotVacuumCleaner extends deviceBase { this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { try { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { onlineStatus, battery } = context; + const { onlineStatus, battery, workingStatus } = context; const { On } = this.LightBulb; - const { BatteryLevel } = this.Battery; + const { BatteryLevel, ChargingState } = this.Battery; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(onlineStatus, battery) = ' + - `Webhook:(${onlineStatus}, ${battery}), ` + - `current:(${On}, ${BatteryLevel})`); + '(onlineStatus, battery, workingStatus) = ' + + `Webhook:(${onlineStatus}, ${battery}, ${workingStatus}), ` + + `current:(${On}, ${BatteryLevel}, ${ChargingState})`); this.LightBulb.On = onlineStatus === 'online' ? true : false; + this.Battery.ChargingState = workingStatus === 'Charging' + ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; this.Battery.BatteryLevel = battery; this.updateHomeKitCharacteristics(); } catch (e: any) { @@ -189,6 +207,8 @@ export class RobotVacuumCleaner extends deviceBase { // BatteryLevel this.Battery.BatteryLevel = Number(deviceStatus.body.battery); + + // StatusLowBattery if (this.Battery.BatteryLevel < 10) { this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW; } else { @@ -197,8 +217,11 @@ export class RobotVacuumCleaner extends deviceBase { if (Number.isNaN(this.Battery.BatteryLevel)) { this.Battery.BatteryLevel = 100; } + // ChargingState + this.Battery.ChargingState = deviceStatus.body.workingStatus === 'Charging' + ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}, ChargingState: ${this.Battery.ChargingState}`); // Firmware Version const version = deviceStatus.body.version?.toString(); @@ -219,8 +242,8 @@ export class RobotVacuumCleaner extends deviceBase { async refreshStatus(): Promise { if (!this.device.enableCloudService && this.OpenAPI) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`); - /*} else if (this.BLE) { - await this.BLERefreshStatus();*/ + } else if (this.BLE) { + await this.BLERefreshStatus(); } else if (this.OpenAPI && this.platform.config.credentials?.token) { await this.openAPIRefreshStatus(); } else { @@ -244,21 +267,19 @@ export class RobotVacuumCleaner extends deviceBase { this.getCustomBLEAddress(switchbot); // Start to monitor advertisement packets (async () => { - await switchbot.startScan({ - model: '?', - id: this.device.bleMac, - }); + // Start to monitor advertisement packets + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }); // Set an event handler switchbot.onadvertisement = (ad: any) => { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`); - if (this.device.bleMac === ad.address && ad.serviceData.model === '?') { + if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); } }; - // Wait 1 seconds + // Wait 10 seconds await switchbot.wait(this.scanDuration * 1000); // Stop to monitor await switchbot.stopScan(); @@ -266,63 +287,16 @@ export class RobotVacuumCleaner extends deviceBase { await this.BLEparseStatus(switchbot.onadvertisement.serviceData); await this.updateHomeKitCharacteristics(); })(); - /*if (switchbot !== false) { - switchbot - .startScan({ - model: this.BLEmodel(), - id: this.device.bleMac, - }) - .then(async () => { - // Set an event handler - switchbot.onadvertisement = async (ad: ad) => { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` + - ` BLE Address Found: ${ad.address}`, - ); - serviceData.state = ad.serviceData.state; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` + - `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` + - `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`, - ); - - if (ad.serviceData) { - this.BLE_IsConnected = true; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - await this.stopScanning(switchbot); - } else { - this.BLE_IsConnected = false; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`); - } - }; - // Wait - return await sleep(this.scanDuration * 1000); - }) - .then(async () => { - // Stop to monitor - await this.stopScanning(switchbot); - }) - .catch(async (e: any) => { - this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); - await this.BLERefreshConnection(switchbot); - }); - } else { + if (switchbot === undefined) { await this.BLERefreshConnection(switchbot); - }*/ + } } - async openAPIRefreshStatus() { + async openAPIRefreshStatus(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -608,6 +582,15 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } + // ChargingState + if (this.Battery.ChargingState === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`); + } else { + this.accessory.context.ChargingState = this.Battery.ChargingState; + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` ChargingState: ${this.Battery.ChargingState}`); + } } async BLEPushConnection() { diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index cca07397..dfe19310 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -19,6 +19,7 @@ import type { device, devicesConfig, serviceData, deviceStatus} from '../setting export class WaterDetector extends deviceBase { // Services private Battery: { + Name: CharacteristicValue Service: Service; BatteryLevel: CharacteristicValue; StatusLowBattery: CharacteristicValue; @@ -26,6 +27,7 @@ export class WaterDetector extends deviceBase { }; private LeakSensor?: { + Name: CharacteristicValue; Service: Service; StatusActive: CharacteristicValue; LeakDetected: CharacteristicValue; @@ -48,49 +50,57 @@ export class WaterDetector extends deviceBase { this.doWaterDetectorUpdate = new Subject(); this.WaterDetectorUpdateInProgress = false; - // Initialize Battery property + // Initialize Battery Service this.Battery = { - Service: accessory.getService(this.hap.Service.Battery) as Service, - BatteryLevel: accessory.context.BatteryLevel || 100, - StatusLowBattery: accessory.context.StatusLowBattery || this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, - ChargingState: accessory.context.ChargingState || this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, + BatteryLevel: accessory.context.BatteryLevel ?? 100, + StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, + ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, }; - // Initialize Leak Sensor property - if (!this.device.waterdetector?.hide_leak) { - this.LeakSensor = { - Service: accessory.getService(this.hap.Service.LeakSensor) as Service, - StatusActive: accessory.context.StatusActive || false, - LeakDetected: accessory.context.LeakDetected || this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, - }; - } + // Initialize Battery Characteristic + this.Battery.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) + .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) + .getCharacteristic(this.hap.Characteristic.BatteryLevel) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); - // Retrieve initial values and updateHomekit - this.refreshStatus(); + this.Battery.Service + .getCharacteristic(this.hap.Characteristic.StatusLowBattery) + .onGet(() => { + return this.Battery.StatusLowBattery; + }); + accessory.context.Battery.Name = this.Battery.Name; - // Leak Sensor Service + // Initialize Leak Sensor Service if (device.waterdetector?.hide_leak) { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; accessory.removeService(this.LeakSensor!.Service); - } else if (!this.LeakSensor?.Service) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Leak Sensor Service`); - const LeakSensorService = `${accessory.displayName} Leak Sensor`; - (this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) - || this.accessory.addService(this.hap.Service.LeakSensor)), LeakSensorService; - - this.LeakSensor!.Service.setCharacteristic(this.hap.Characteristic.Name, LeakSensorService); } else { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Leak Sensor Service Not Added`); - } + this.LeakSensor = { + Name: accessory.context.LeakSensor.Name ?? `${accessory.displayName} Leak Sensor`, + Service: accessory.getService(this.hap.Service.LeakSensor) ?? this.accessory.addService(this.hap.Service.LeakSensor) as Service, + StatusActive: accessory.context.StatusActive ?? false, + LeakDetected: accessory.context.LeakDetected ?? this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, + }; - // Battery Service - const BatteryService = `${accessory.displayName} Battery`; - (this.Battery.Service = this.accessory.getService(this.hap.Service.Battery) - || accessory.addService(this.hap.Service.Battery)), BatteryService; + // Initialize LeakSensor Characteristic + this.LeakSensor!.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LeakSensor.Name) + .setCharacteristic(this.hap.Characteristic.StatusActive, true) + .getCharacteristic(this.hap.Characteristic.LeakDetected) + .onGet(() => { + return this.LeakSensor!.LeakDetected; + }); + accessory.context.LeakSensor.Name = this.LeakSensor.Name; + } - this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, BatteryService); - this.Battery.Service.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); + // Retrieve initial values and updateHomekit + this.refreshStatus(); // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); From 21339be59baf99205b863f419681c7a4c191f133 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 19 May 2024 18:17:21 -0500 Subject: [PATCH 62/73] fixe name Characteristic --- src/device/blindtilt.ts | 12 ++++++------ src/device/bot.ts | 2 +- src/device/ceilinglight.ts | 4 ++-- src/device/colorbulb.ts | 4 ++-- src/device/contact.ts | 16 ++++++++-------- src/device/curtain.ts | 12 ++++++------ src/device/fan.ts | 8 ++++---- src/device/hub.ts | 12 ++++++------ src/device/humidifier.ts | 8 ++++---- src/device/iosensor.ts | 12 ++++++------ src/device/lightstrip.ts | 4 ++-- src/device/lock.ts | 16 ++++++++-------- src/device/meter.ts | 12 ++++++------ src/device/meterplus.ts | 12 ++++++------ src/device/motion.ts | 12 ++++++------ src/device/plug.ts | 4 ++-- src/device/robotvacuumcleaner.ts | 8 ++++---- src/device/waterdetector.ts | 8 ++++---- 18 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 693ac99d..7689be53 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -68,7 +68,7 @@ export class BlindTilt extends deviceBase { // Initialize WindowCovering Service this.WindowCovering = { - Name: accessory.context.WindowCovering.Name ?? accessory.displayName, + Name: accessory.context.WindowCoveringName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition ?? 100, @@ -117,7 +117,7 @@ export class BlindTilt extends deviceBase { return this.WindowCovering.TargetHorizontalTiltAngle; }) .onSet(this.TargetHorizontalTiltAngleSet.bind(this)); - accessory.context.WindowCovering.Name = this.WindowCovering.Name; + accessory.context.WindowCoveringName = this.WindowCovering.Name; // Initialize WindowCovering CurrentHorizontalTiltAngle Characteristic this.WindowCovering.Service @@ -133,7 +133,7 @@ export class BlindTilt extends deviceBase { // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -144,7 +144,7 @@ export class BlindTilt extends deviceBase { this.Battery.Service .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize LightSensor Service if (device.blindTilt?.hide_lightsensor) { @@ -153,7 +153,7 @@ export class BlindTilt extends deviceBase { accessory.removeService(this.LightSensor!.Service); } else { this.LightSensor = { - Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; @@ -166,7 +166,7 @@ export class BlindTilt extends deviceBase { .onGet(() => { return this.LightSensor?.CurrentAmbientLightLevel ?? 0.0001; }); - accessory.context.LightSensor.Name = this.LightSensor.Name; + accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/bot.ts b/src/device/bot.ts index 6d930817..9ad4c24a 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -124,7 +124,7 @@ export class Bot extends deviceBase { .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery) .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // deviceType if (this.botDeviceType === 'switch') { diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index feb31678..077a3319 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -53,7 +53,7 @@ export class CeilingLight extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Name: accessory.context.LightBulbName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -141,7 +141,7 @@ export class CeilingLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; + accessory.context.LightBulbName = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 72fb3d03..d36e9aeb 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -54,7 +54,7 @@ export class ColorBulb extends deviceBase { // Initialize LightBulb property this.LightBulb = { - Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Name: accessory.context.LightBulbName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -138,7 +138,7 @@ export class ColorBulb extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; + accessory.context.LightBulbName = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/contact.ts b/src/device/contact.ts index a80eeb16..908523d2 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -59,7 +59,7 @@ export class Contact extends deviceBase { // Initialize Contact Sensor property this.ContactSensor = { - Name: accessory.context.ContactSensor.Name ?? accessory.displayName, + Name: accessory.context.ContactSensorName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.ContactSensor) ?? accessory.addService(this.hap.Service.ContactSensor) as Service, ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; @@ -72,11 +72,11 @@ export class Contact extends deviceBase { .onGet(() => { return this.ContactSensor.ContactSensorState; }); - accessory.context.ContactSensor.Name = this.ContactSensor.Name; + accessory.context.ContactSensorName = this.ContactSensor.Name; // Initialize Battery property this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -97,7 +97,7 @@ export class Contact extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize Motion Sensor Service if (this.device.contact?.hide_motionsensor) { @@ -106,7 +106,7 @@ export class Contact extends deviceBase { accessory.removeService(this.MotionSensor!.Service); } else { this.MotionSensor = { - Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, + Name: accessory.context.MotionSensorName ?? `${accessory.displayName} Motion Sensor`, Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected ?? false, }; @@ -119,7 +119,7 @@ export class Contact extends deviceBase { .onGet(() => { return this.MotionSensor!.MotionDetected; }); - accessory.context.MotionSensor.Name = this.MotionSensor.Name; + accessory.context.MotionSensorName = this.MotionSensor.Name; } // Initialize Light Sensor Service @@ -129,7 +129,7 @@ export class Contact extends deviceBase { accessory.removeService(this.LightSensor!.Service); } else { this.LightSensor = { - Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; @@ -142,7 +142,7 @@ export class Contact extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensor.Name = this.LightSensor.Name; + accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/curtain.ts b/src/device/curtain.ts index b41dd96c..0bbd2907 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -61,7 +61,7 @@ export class Curtain extends deviceBase { // Initialize LightBulb property this.WindowCovering = { - Name: accessory.context.WindowCovering.Name ?? accessory.displayName, + Name: accessory.context.WindowCoveringName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition ?? 100, @@ -77,7 +77,7 @@ export class Curtain extends deviceBase { .onGet(() => { return this.WindowCovering.PositionState; }); - accessory.context.WindowCovering.Name = this.WindowCovering.Name; + accessory.context.WindowCoveringName = this.WindowCovering.Name; // Initialize WindowCovering CurrentPosition this.WindowCovering.Service @@ -116,7 +116,7 @@ export class Curtain extends deviceBase { // Initialize Battery property this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -130,7 +130,7 @@ export class Curtain extends deviceBase { .onGet(() => { return this.Battery.BatteryLevel; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; this.Battery.Service .getCharacteristic(this.hap.Characteristic.StatusLowBattery) @@ -151,7 +151,7 @@ export class Curtain extends deviceBase { accessory.removeService(this.LightSensor!.Service); } else { this.LightSensor = { - Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; @@ -164,7 +164,7 @@ export class Curtain extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel!; }); - accessory.context.LightSensor.Name = this.LightSensor.Name; + accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/fan.ts b/src/device/fan.ts index 3a51da38..8f46d272 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -45,7 +45,7 @@ export class Fan extends deviceBase { // Initialize Fan Service this.Fan = { - Name: accessory.context.Fan.Name ?? accessory.displayName, + Name: accessory.context.FanName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED, @@ -75,11 +75,11 @@ export class Fan extends deviceBase { return this.Fan.SwingMode; }) .onSet(this.SwingModeSet.bind(this)); - accessory.context.Fan.Name = this.Fan.Name; + accessory.context.FanName = this.Fan.Name; // Initialize Battery property this.Battery = { - Name: accessory.context.Battery.Name ?? accessory.displayName, + Name: accessory.context.BatteryName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -107,7 +107,7 @@ export class Fan extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/hub.ts b/src/device/hub.ts index 6d2d8baa..43422e73 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -53,7 +53,7 @@ export class Hub extends deviceBase { accessory.removeService(this.TemperatureSensor!.Service); } else { this.TemperatureSensor = { - Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 0, }; @@ -72,7 +72,7 @@ export class Hub extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; + accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service @@ -82,7 +82,7 @@ export class Hub extends deviceBase { accessory.removeService(this.HumiditySensor!.Service); } else { this.HumiditySensor = { - Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 0, }; @@ -97,7 +97,7 @@ export class Hub extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; + accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Initialize Light Sensor Service @@ -107,7 +107,7 @@ export class Hub extends deviceBase { accessory.removeService(this.LightSensor!.Service); } else { this.LightSensor = { - Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; @@ -122,7 +122,7 @@ export class Hub extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensor.Name = this.LightSensor.Name; + accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 3cfb35c6..14bdb3cf 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -49,7 +49,7 @@ export class Humidifier extends deviceBase { // Initialize the HumidifierDehumidifier Service this.HumidifierDehumidifier = { - Name: accessory.context.HumidifierDehumidifier.Name ?? accessory.displayName, + Name: accessory.context.HumidifierDehumidifierName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.HumidifierDehumidifier) ?? accessory.addService(this.hap.Service.HumidifierDehumidifier) as Service, Active: accessory.context.Active ?? this.hap.Characteristic.Active.ACTIVE, @@ -98,7 +98,7 @@ export class Humidifier extends deviceBase { return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; }) .onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this)); - accessory.context.HumidifierDehumidifier.Name = this.HumidifierDehumidifier.Name; + accessory.context.HumidifierDehumidifierName = this.HumidifierDehumidifier.Name; // Initialize the Temperature Sensor Service if (device.humidifier?.hide_temperature) { @@ -107,7 +107,7 @@ export class Humidifier extends deviceBase { accessory.removeService(this.TemperatureSensor!.Service); } else { this.TemperatureSensor = { - Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; @@ -125,7 +125,7 @@ export class Humidifier extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; + accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 84ab6098..0c97a4c6 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -54,7 +54,7 @@ export class IOSensor extends deviceBase { // Initialize Battery property this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -74,7 +74,7 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // InitializeTemperature Sensor Service if (device.iosensor?.hide_temperature) { @@ -83,7 +83,7 @@ export class IOSensor extends deviceBase { accessory.removeService(this.TemperatureSensor!.Service); } else { this.TemperatureSensor = { - Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; @@ -102,7 +102,7 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; + accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service @@ -112,7 +112,7 @@ export class IOSensor extends deviceBase { accessory.removeService(this.HumiditySensor!.Service); } else { this.HumiditySensor = { - Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; @@ -127,7 +127,7 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; + accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index e3cb8365..e7ad7ae5 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -47,7 +47,7 @@ export class StripLight extends deviceBase { // Initialize the LightBulb Service this.LightBulb = { - Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Name: accessory.context.LightBulbName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -137,7 +137,7 @@ export class StripLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; + accessory.context.LightBulbName = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/lock.ts b/src/device/lock.ts index b5a891a3..a0ffc7d2 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -52,7 +52,7 @@ export class Lock extends deviceBase { // Initialize LockMechanism Service this.LockMechanism = { - Name: accessory.context.LockMechanism.Name ?? accessory.displayName, + Name: accessory.context.LockMechanismName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, LockTargetState: accessory.context.LockTargetState ?? this.hap.Characteristic.LockTargetState.SECURED, LockCurrentState: accessory.context.LockCurrentState ?? this.hap.Characteristic.LockCurrentState.SECURED, @@ -66,11 +66,11 @@ export class Lock extends deviceBase { return this.LockMechanism.LockTargetState; }) .onSet(this.LockTargetStateSet.bind(this)); - accessory.context.LockMechanism.Name = this.LockMechanism.Name; + accessory.context.LockMechanismName = this.LockMechanism.Name; // Initialize Battery property this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -90,7 +90,7 @@ export class Lock extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Contact Sensor Service if (device.lock?.hide_contactsensor) { @@ -99,7 +99,7 @@ export class Lock extends deviceBase { accessory.removeService(this.ContactSensor!.Service); } else { this.ContactSensor = { - Name: accessory.context.ContactSensor.Name ?? `${accessory.displayName} Contact Sensor`, + Name: accessory.context.ContactSensorName ?? `${accessory.displayName} Contact Sensor`, Service: accessory.getService(this.hap.Service.ContactSensor) ?? this.accessory.addService(this.hap.Service.ContactSensor) as Service, ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; @@ -112,7 +112,7 @@ export class Lock extends deviceBase { .onGet(() => { return this.ContactSensor!.ContactSensorState; }); - accessory.context.ContactSensor.Name = this.ContactSensor.Name; + accessory.context.ContactSensorName = this.ContactSensor.Name; } // Initialize Latch Button Service @@ -122,7 +122,7 @@ export class Lock extends deviceBase { accessory.removeService(this.Switch!.Service); } else { this.Switch = { - Name: accessory.context.Switch.Name ?? `${accessory.displayName} Latch`, + Name: accessory.context.SwitchName ?? `${accessory.displayName} Latch`, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; @@ -135,7 +135,7 @@ export class Lock extends deviceBase { return this.Switch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Switch.Name = this.Switch.Name; + accessory.context.SwitchName = this.Switch.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/meter.ts b/src/device/meter.ts index f57d5fe1..a5e3490b 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -49,7 +49,7 @@ export class Meter extends deviceBase { // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -69,7 +69,7 @@ export class Meter extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { @@ -78,7 +78,7 @@ export class Meter extends deviceBase { accessory.removeService(this.TemperatureSensor!.Service); } else { this.TemperatureSensor = { - Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; @@ -97,7 +97,7 @@ export class Meter extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; + accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { @@ -106,7 +106,7 @@ export class Meter extends deviceBase { accessory.removeService(this.HumiditySensor!.Service); } else { this.HumiditySensor = { - Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; @@ -121,7 +121,7 @@ export class Meter extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; + accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 2b61aa56..1b121aa0 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -53,7 +53,7 @@ export class MeterPlus extends deviceBase { // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -73,7 +73,7 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { @@ -82,7 +82,7 @@ export class MeterPlus extends deviceBase { accessory.removeService(this.TemperatureSensor!.Service); } else { this.TemperatureSensor = { - Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; @@ -101,7 +101,7 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; + accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { @@ -110,7 +110,7 @@ export class MeterPlus extends deviceBase { accessory.removeService(this.HumiditySensor!.Service); } else { this.HumiditySensor = { - Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; @@ -125,7 +125,7 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - accessory.context.HumiditySensor.Name = this.HumiditySensor.Name; + accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/motion.ts b/src/device/motion.ts index c04e4f88..02bf3dc1 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -53,7 +53,7 @@ export class Motion extends deviceBase { // Initialize Motion Sensor property this.MotionSensor = { - Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, + Name: accessory.context.MotionSensorName ?? `${accessory.displayName} Motion Sensor`, Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected ?? false, }; @@ -66,11 +66,11 @@ export class Motion extends deviceBase { .onGet(() => { return this.MotionSensor.MotionDetected; }); - accessory.context.MotionSensor.Name = this.MotionSensor.Name; + accessory.context.MotionSensorName = this.MotionSensor.Name; // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -91,7 +91,7 @@ export class Motion extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize Light Sensor Service if (device.motion?.hide_lightsensor) { @@ -100,7 +100,7 @@ export class Motion extends deviceBase { accessory.removeService(this.LightSensor!.Service); } else { this.LightSensor = { - Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0, }; @@ -113,7 +113,7 @@ export class Motion extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensor.Name = this.LightSensor.Name; + accessory.context.LightSensorName = this.LightSensor.Name; }; diff --git a/src/device/plug.ts b/src/device/plug.ts index ca3c9dcd..32a8efd4 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -34,7 +34,7 @@ export class Plug extends deviceBase { // Initialize Outlet Service this.Outlet = { - Name: accessory.context.Outlet.Name ?? accessory.displayName, + Name: accessory.context.OutletName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; @@ -47,7 +47,7 @@ export class Plug extends deviceBase { return this.Outlet.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Outlet.Name = this.Outlet.Name; + accessory.context.OutletName = this.Outlet.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 87d79c3d..573c6884 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -45,7 +45,7 @@ export class RobotVacuumCleaner extends deviceBase { // Initialize Lightbulb Service this.LightBulb = { - Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Name: accessory.context.LightBulbName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Brightness: accessory.context.Brightness ?? 0, @@ -74,11 +74,11 @@ export class RobotVacuumCleaner extends deviceBase { return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; + accessory.context.LightBulbName = this.LightBulb.Name; // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -104,7 +104,7 @@ export class RobotVacuumCleaner extends deviceBase { .onGet(() => { return this.Battery.ChargingState; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index dfe19310..79386ce8 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -52,7 +52,7 @@ export class WaterDetector extends deviceBase { // Initialize Battery Service this.Battery = { - Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, + Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -73,7 +73,7 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; + accessory.context.BatteryName = this.Battery.Name; // Initialize Leak Sensor Service if (device.waterdetector?.hide_leak) { @@ -82,7 +82,7 @@ export class WaterDetector extends deviceBase { accessory.removeService(this.LeakSensor!.Service); } else { this.LeakSensor = { - Name: accessory.context.LeakSensor.Name ?? `${accessory.displayName} Leak Sensor`, + Name: accessory.context.LeakSensorName ?? `${accessory.displayName} Leak Sensor`, Service: accessory.getService(this.hap.Service.LeakSensor) ?? this.accessory.addService(this.hap.Service.LeakSensor) as Service, StatusActive: accessory.context.StatusActive ?? false, LeakDetected: accessory.context.LeakDetected ?? this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, @@ -96,7 +96,7 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.LeakSensor!.LeakDetected; }); - accessory.context.LeakSensor.Name = this.LeakSensor.Name; + accessory.context.LeakSensorName = this.LeakSensor.Name; } // Retrieve initial values and updateHomekit From ed45876c3e90873d06d3822d1fc8972a5d3ad449 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 19 May 2024 18:21:52 -0500 Subject: [PATCH 63/73] Update colorbulb.ts --- src/device/colorbulb.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index d36e9aeb..ac066d3b 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -47,14 +47,15 @@ export class ColorBulb extends deviceBase { // default placeholders this.adaptiveLighting(device); - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doColorBulbUpdate = new Subject(); this.colorBulbUpdateInProgress = false; + accessory.context.LightBulb = {}; + // Initialize LightBulb property this.LightBulb = { - Name: accessory.context.LightBulbName ?? accessory.displayName, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -138,7 +139,7 @@ export class ColorBulb extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulbName = this.LightBulb.Name; + accessory.context.LightBulb.Name = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); From c2f7c83206f541d2b6f0fdd41b63510919d50e78 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 19 May 2024 18:26:55 -0500 Subject: [PATCH 64/73] Update platform.ts --- src/platform.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/platform.ts b/src/platform.ts index 22400052..7d274709 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -735,21 +735,18 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); this.createStripLight(device); break; - case 'Indoor Cam': + case 'Battery Circulator Fan': this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); - this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`); + this.createFan(device); break; case 'Remote': + case 'Indoor Cam': case 'remote with screen+': - this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId} is Not Supported.`); - break; - case 'Battery Circulator Fan': - this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`); - this.createFan(device); + this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}, is currently not supported, device: ${JSON.stringify(device)}`); break; default: this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`, - + 'Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest'); + + `Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}` ); } } From 33d5a367a53fc4b940cee43d12b3cdb9b65816b3 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 14:01:56 -0500 Subject: [PATCH 65/73] add Name Characteristics to IR --- src/device/blindtilt.ts | 6 +- src/device/bot.ts | 3 +- src/device/colorbulb.ts | 5 +- src/device/curtain.ts | 19 +- src/device/motion.ts | 2 +- src/device/waterdetector.ts | 8 +- src/irdevice/airconditioner.ts | 158 +++++++------- src/irdevice/airpurifier.ts | 67 ++++-- src/irdevice/camera.ts | 27 +-- src/irdevice/fan.ts | 46 ++-- src/irdevice/light.ts | 112 ++++++---- src/irdevice/other.ts | 383 ++++++++++++++++++++------------- src/irdevice/tv.ts | 131 ++++++----- src/irdevice/vacuumcleaner.ts | 28 +-- src/irdevice/waterheater.ts | 33 +-- 15 files changed, 582 insertions(+), 446 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 7689be53..0bbc97c7 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -66,9 +66,10 @@ export class BlindTilt extends deviceBase { this.blindTiltUpdateInProgress = false; this.setNewTarget = false; + accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; // Initialize WindowCovering Service this.WindowCovering = { - Name: accessory.context.WindowCoveringName ?? accessory.displayName, + Name: accessory.context.WindowCovering.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition ?? 100, @@ -76,6 +77,7 @@ export class BlindTilt extends deviceBase { TargetHorizontalTiltAngle: accessory.context.TargetHorizontalTiltAngle ?? 90, CurrentHorizontalTiltAngle: accessory.context.CurrentHorizontalTiltAngle ?? 90, }; + accessory.context.WindowCovering = this.WindowCovering as object; // Initialize WindowCovering Characteristics this.WindowCovering.Service @@ -117,7 +119,7 @@ export class BlindTilt extends deviceBase { return this.WindowCovering.TargetHorizontalTiltAngle; }) .onSet(this.TargetHorizontalTiltAngleSet.bind(this)); - accessory.context.WindowCoveringName = this.WindowCovering.Name; + accessory.context.WindowCovering.Name = this.WindowCovering.Name; // Initialize WindowCovering CurrentHorizontalTiltAngle Characteristic this.WindowCovering.Service diff --git a/src/device/bot.ts b/src/device/bot.ts index 9ad4c24a..c8c03b13 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -111,9 +111,10 @@ export class Bot extends deviceBase { this.doBotUpdate = new Subject(); this.botUpdateInProgress = false; + accessory.context.Battery = accessory.context.Battery ?? {}; // Initialize Battery property this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index ac066d3b..0c0da33e 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -51,8 +51,7 @@ export class ColorBulb extends deviceBase { this.doColorBulbUpdate = new Subject(); this.colorBulbUpdateInProgress = false; - accessory.context.LightBulb = {}; - + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; // Initialize LightBulb property this.LightBulb = { Name: accessory.context.LightBulb.Name ?? accessory.displayName, @@ -63,6 +62,8 @@ export class ColorBulb extends deviceBase { Brightness: accessory.context.Brightness ?? 0, ColorTemperature: accessory.context.ColorTemperature ?? 140, }; + accessory.context.LightBulb = this.LightBulb as object; + this.errorLog(`${device.deviceType}: ${accessory.displayName} LightBulb Context: ${JSON.stringify(accessory.context.LightBulb)}`); // Adaptive Lighting if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) { diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 0bbd2907..03d8e8cd 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -59,15 +59,17 @@ export class Curtain extends deviceBase { this.curtainUpdateInProgress = false; this.setNewTarget = false; - // Initialize LightBulb property + // Initialize WindowCovering Service + accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; this.WindowCovering = { - Name: accessory.context.WindowCoveringName ?? accessory.displayName, + Name: accessory.context.WindowCovering.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED, TargetPosition: accessory.context.TargetPosition ?? 100, CurrentPosition: accessory.context.CurrentPosition ?? 100, HoldPosition: accessory.context.HoldPosition ?? false, }; + accessory.context.WindowCovering = this.WindowCovering as object; // Initialize WindowCovering Service this.WindowCovering.Service. @@ -77,7 +79,6 @@ export class Curtain extends deviceBase { .onGet(() => { return this.WindowCovering.PositionState; }); - accessory.context.WindowCoveringName = this.WindowCovering.Name; // Initialize WindowCovering CurrentPosition this.WindowCovering.Service @@ -114,14 +115,16 @@ export class Curtain extends deviceBase { }) .onSet(this.HoldPositionSet.bind(this)); - // Initialize Battery property + // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Service this.Battery.Service @@ -130,7 +133,6 @@ export class Curtain extends deviceBase { .onGet(() => { return this.Battery.BatteryLevel; }); - accessory.context.BatteryName = this.Battery.Name; this.Battery.Service .getCharacteristic(this.hap.Characteristic.StatusLowBattery) @@ -150,11 +152,13 @@ export class Curtain extends deviceBase { this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); } else { + accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { - Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; + accessory.context.LightSensor = this.LightSensor as object; // Initialize LightSensor Characteristic this.LightSensor.Service @@ -164,7 +168,6 @@ export class Curtain extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel!; }); - accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/motion.ts b/src/device/motion.ts index 02bf3dc1..0898f71e 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -102,7 +102,7 @@ export class Motion extends deviceBase { this.LightSensor = { Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, - CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0, + CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; // Initialize LightSensor Characteristics diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 79386ce8..dfe19310 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -52,7 +52,7 @@ export class WaterDetector extends deviceBase { // Initialize Battery Service this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, @@ -73,7 +73,7 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; + accessory.context.Battery.Name = this.Battery.Name; // Initialize Leak Sensor Service if (device.waterdetector?.hide_leak) { @@ -82,7 +82,7 @@ export class WaterDetector extends deviceBase { accessory.removeService(this.LeakSensor!.Service); } else { this.LeakSensor = { - Name: accessory.context.LeakSensorName ?? `${accessory.displayName} Leak Sensor`, + Name: accessory.context.LeakSensor.Name ?? `${accessory.displayName} Leak Sensor`, Service: accessory.getService(this.hap.Service.LeakSensor) ?? this.accessory.addService(this.hap.Service.LeakSensor) as Service, StatusActive: accessory.context.StatusActive ?? false, LeakDetected: accessory.context.LeakDetected ?? this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, @@ -96,7 +96,7 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.LeakSensor!.LeakDetected; }); - accessory.context.LeakSensorName = this.LeakSensor.Name; + accessory.context.LeakSensor.Name = this.LeakSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index cf65019d..33aa5d6f 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class AirConditioner extends irdeviceBase { // Services private HeaterCooler: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; CurrentHeaterCoolerState: CharacteristicValue; @@ -27,7 +28,9 @@ export class AirConditioner extends irdeviceBase { RotationSpeed: CharacteristicValue; }; + meter?: PlatformAccessory; private HumiditySensor?: { + Name: CharacteristicValue; Service: Service; CurrentRelativeHumidity: CharacteristicValue; }; @@ -46,7 +49,6 @@ export class AirConditioner extends irdeviceBase { set_min_heat?: number; set_max_cool?: number; set_min_cool?: number; - meter?: PlatformAccessory; constructor( readonly platform: SwitchBotPlatform, @@ -58,64 +60,54 @@ export class AirConditioner extends irdeviceBase { // default placeholders this.getAirConditionerConfigSettings(accessory, device); - // Initialize HeaterCooler property - this.HeaterCooler = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, - CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState || this.hap.Characteristic.CurrentHeaterCoolerState.IDLE, - TargetHeaterCoolerState: accessory.context.TargetHeaterCoolerState || this.hap.Characteristic.TargetHeaterCoolerState.AUTO, - CurrentTemperature: accessory.context.CurrentTemperature || 24, - ThresholdTemperature: accessory.context.ThresholdTemperature || 24, - RotationSpeed: accessory.context.RotationSpeed || 4, - }; + this.ValidValues = this.hide_automode ? [1, 2] : [0, 1, 2]; - // Initialize HumiditySensor property - if (this.device.irair?.meterType && this.device.irair?.meterId) { - const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); - this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); - this.HumiditySensor = { - Service: this.meter!.getService(this.hap.Service.HumiditySensor) as Service, - CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, - }; - } - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const HeaterCoolerService = `${accessory.displayName} ${device.remoteType}`; - (this.HeaterCooler.Service = accessory.getService(this.hap.Service.HeaterCooler) - || accessory.addService(this.hap.Service.HeaterCooler)), HeaterCoolerService; + // Initialize HeaterCooler Service + if (!accessory.context.HeaterCooler) { + accessory.context.HeaterCooler = {}; + } + this.HeaterCooler = { + Name: accessory.context.HeaterCooler.Name ?? `${accessory.displayName} ${device.remoteType}`, + Service: accessory.getService(this.hap.Service.HeaterCooler) ?? accessory.addService(this.hap.Service.HeaterCooler) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, + CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState ?? this.hap.Characteristic.CurrentHeaterCoolerState.IDLE, + TargetHeaterCoolerState: accessory.context.TargetHeaterCoolerState ?? this.hap.Characteristic.TargetHeaterCoolerState.AUTO, + CurrentTemperature: accessory.context.CurrentTemperature ?? 24, + ThresholdTemperature: accessory.context.ThresholdTemperature ?? 24, + RotationSpeed: accessory.context.RotationSpeed ?? 4, + }; - this.HeaterCooler.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // handle on / off events using the Active characteristic - this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.Active) + this.HeaterCooler.Service + .setCharacteristic(this.hap.Characteristic.Name, this.HeaterCooler.Name) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.HeaterCooler.Active; + }) .onSet(this.ActiveSet.bind(this)); - this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.CurrentTemperature) - .onGet(this.CurrentTemperatureGet.bind(this)); - - this.ValidValues = this.hide_automode ? [1, 2] : [0, 1, 2]; - - if (this.device.irair?.meterType && this.device.irair?.meterId) { - const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); - this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); - } - - if (this.meter) { - this.HumiditySensor!.Service.getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) - .onGet(this.CurrentRelativeHumidityGet.bind(this)); - } + this.HeaterCooler.Service + .getCharacteristic(this.hap.Characteristic.CurrentTemperature) + .onGet(async () => { + return await this.CurrentTemperatureGet(); + }); this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.TargetHeaterCoolerState) .setProps({ validValues: this.ValidValues, }) - .onGet(this.TargetHeaterCoolerStateGet.bind(this)) + .onGet(async () => { + return await this.TargetHeaterCoolerStateGet(); + }) .onSet(this.TargetHeaterCoolerStateSet.bind(this)); - this.HeaterCooler.Service.getCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState) - .onGet(this.CurrentHeaterCoolerStateGet.bind(this)); + this.HeaterCooler.Service + .getCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState) + .onGet(async () => { + return await this.CurrentHeaterCoolerStateGet(); + }); this.HeaterCooler.Service .getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature) @@ -124,7 +116,9 @@ export class AirConditioner extends irdeviceBase { maxValue: this.set_max_heat, minStep: 0.5, }) - .onGet(this.ThresholdTemperatureGet.bind(this)) + .onGet(async () => { + return await this.ThresholdTemperatureGet(); + }) .onSet(this.ThresholdTemperatureSet.bind(this)); this.HeaterCooler.Service @@ -134,7 +128,9 @@ export class AirConditioner extends irdeviceBase { maxValue: this.set_max_cool, minStep: 0.5, }) - .onGet(this.ThresholdTemperatureGet.bind(this)) + .onGet(async () => { + return await this.ThresholdTemperatureGet(); + }) .onSet(this.ThresholdTemperatureSet.bind(this)); this.HeaterCooler.Service @@ -145,8 +141,36 @@ export class AirConditioner extends irdeviceBase { minValue: 1, maxValue: 4, }) - .onGet(this.RotationSpeedGet.bind(this)) + .onGet(async () => { + return await this.RotationSpeedGet(); + }) .onSet(this.RotationSpeedSet.bind(this)); + accessory.context.HeaterCooler.Name = this.HeaterCooler.Name; + + // Initialize HumiditySensor property + + if (this.device.irair?.meterType && this.device.irair?.meterId) { + const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); + this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); + this.HumiditySensor = { + Name: this.meter!.displayName, + Service: this.meter!.getService(this.hap.Service.HumiditySensor) ?? this.meter!.addService(this.hap.Service.HumiditySensor) as Service, + CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, + }; + } + + if (this.device.irair?.meterType && this.device.irair?.meterId) { + const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); + this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); + } + + if (this.meter) { + this.HumiditySensor!.Service + .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) + .onGet(async () => { + return await this.CurrentRelativeHumidityGet(); + }); + } } /** @@ -545,43 +569,7 @@ export class AirConditioner extends irdeviceBase { this.HeaterCooler.RotationSpeed = this.HeaterCooler.RotationSpeed || this.accessory.context.RotationSpeed; } - if (this.device.irair?.hide_automode) { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } else { - this.hide_automode = this.device.irair?.hide_automode; - this.accessory.context.hide_automode = this.hide_automode; - } - - if (this.device.irair?.set_max_heat) { - this.set_max_heat = this.device.irair?.set_max_heat; - this.accessory.context.set_max_heat = this.set_max_heat; - } else { - this.set_max_heat = 35; - this.accessory.context.set_max_heat = this.set_max_heat; - } - if (this.device.irair?.set_min_heat) { - this.set_min_heat = this.device.irair?.set_min_heat; - this.accessory.context.set_min_heat = this.set_min_heat; - } else { - this.set_min_heat = 0; - this.accessory.context.set_min_heat = this.set_min_heat; - } - - if (this.device.irair?.set_max_cool) { - this.set_max_cool = this.device.irair?.set_max_cool; - this.accessory.context.set_max_cool = this.set_max_cool; - } else { - this.set_max_cool = 35; - this.accessory.context.set_max_cool = this.set_max_cool; - } - if (this.device.irair?.set_min_cool) { - this.set_min_cool = this.device.irair?.set_min_cool; - this.accessory.context.set_min_cool = this.set_min_cool; - } else { - this.set_min_cool = 0; - this.accessory.context.set_min_cool = this.set_min_cool; - } + await this.getAirConditionerConfigSettings(this.accessory, this.device); if (this.meter) { if (this.HumiditySensor!.CurrentRelativeHumidity === undefined && this.accessory.context.CurrentRelativeHumidity === undefined) { diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index adbb13e9..dd2b1f19 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class AirPurifier extends irdeviceBase { // Services private AirPurifier: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; RotationSpeed: CharacteristicValue; @@ -26,6 +27,7 @@ export class AirPurifier extends irdeviceBase { }; private TemperatureSensor: { + Name: CharacteristicValue; Service: Service; CurrentTemperature: CharacteristicValue; }; @@ -54,36 +56,59 @@ export class AirPurifier extends irdeviceBase { ) { super(platform, accessory, device); + if (!accessory.context.AirPurifier) { + accessory.context.AirPurifier = {}; + } + // Initialize AirPurifier property this.AirPurifier = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, - RotationSpeed: accessory.context.RotationSpeed || 0, - CurrentAirPurifierState: accessory.context.CurrentAirPurifierState || this.hap.Characteristic.CurrentAirPurifierState.INACTIVE, - TargetAirPurifierState: accessory.context.TargetAirPurifierState || this.hap.Characteristic.TargetAirPurifierState.AUTO, + Name: accessory.context.AirPurifier.Name ?? `${accessory.displayName} Air Purifier`, + Service: accessory.getService(this.hap.Service.AirPurifier) ?? accessory.addService(this.hap.Service.AirPurifier) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, + RotationSpeed: accessory.context.RotationSpeed ?? 0, + CurrentAirPurifierState: accessory.context.CurrentAirPurifierState ?? this.hap.Characteristic.CurrentAirPurifierState.INACTIVE, + TargetAirPurifierState: accessory.context.TargetAirPurifierState ?? this.hap.Characteristic.TargetAirPurifierState.AUTO, }; + this.AirPurifier.Service + .setCharacteristic(this.hap.Characteristic.Name, this.AirPurifier.Name) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.AirPurifier.Active; + }) + .onSet(this.ActiveSet.bind(this)); + + this.AirPurifier.Service + .getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState) + .onGet(() => { + return this.CurrentAirPurifierStateGet(); + }); + + this.AirPurifier.Service + .getCharacteristic(this.hap.Characteristic.TargetAirPurifierState) + .onGet(() => { + return this.AirPurifier.TargetAirPurifierState; + }) + .onSet(this.TargetAirPurifierStateSet.bind(this)); + accessory.context.AirPurifier.Name = this.AirPurifier.Name; + + if (!accessory.context.TemperatureSensor) { + accessory.context.TemperatureSensor = {}; + } // Initialize TemperatureSensor property this.TemperatureSensor = { - Service: accessory.getService(this.hap.Service.TemperatureSensor) as Service, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, + Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 24, }; - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const AirPurifierService = `${accessory.displayName} Air Purifier`; - (this.AirPurifier.Service = accessory.getService(this.hap.Service.AirPurifier) - || accessory.addService(this.hap.Service.AirPurifier)), AirPurifierService; - - this.AirPurifier.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // handle on / off events using the Active characteristic - this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - - this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState).onGet(() => { - return this.CurrentAirPurifierStateGet(); - }); - - this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.TargetAirPurifierState).onSet(this.TargetAirPurifierStateSet.bind(this)); + this.TemperatureSensor.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) + .getCharacteristic(this.hap.Characteristic.CurrentTemperature) + .onGet(() => { + return this.TemperatureSensor.CurrentTemperature; + }); + accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } async ActiveSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 220fdeff..eb5f7620 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class Camera extends irdeviceBase { // Services private Switch: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -29,22 +30,24 @@ export class Camera extends irdeviceBase { ) { super(platform, accessory, device); + if (!accessory.context.Switch) { + accessory.context.Switch = {}; + } // Initialize Switch property this.Switch = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Switch.Name ?? `${accessory.displayName} Camera`, + Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, + On: accessory.context.On ?? false, }; - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const SwitchService = `${accessory.displayName} Camera`; - (this.Switch.Service = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), SwitchService; - - this.Switch.Service.setCharacteristic(this.hap.Characteristic.Name, SwitchService); - - // handle on / off events using the On characteristic - this.Switch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Switch.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Switch.Name = this.Switch.Name; } async OnSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 6be9d9e3..8e81d26a 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class IRFan extends irdeviceBase { // Services private Fan: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; SwingMode: CharacteristicValue; @@ -32,26 +33,28 @@ export class IRFan extends irdeviceBase { ) { super(platform, accessory, device); + if (!accessory.context.Fan) { + accessory.context.Fan = {}; + } + // Initialize Switch property this.Fan = { - Service: accessory.getService(this.hap.Service.Lightbulb) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, - SwingMode: accessory.context.SwingMode || this.hap.Characteristic.SwingMode.SWING_DISABLED, - RotationSpeed: accessory.context.RotationSpeed || 0, - RotationDirection: accessory.context.On || this.hap.Characteristic.RotationDirection.CLOCKWISE, + Name: accessory.context.Fan.Name ?? `${accessory.displayName} Fan`, + Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, + SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED, + RotationSpeed: accessory.context.RotationSpeed ?? 0, + RotationDirection: accessory.context.RotationDirection ?? this.hap.Characteristic.RotationDirection.CLOCKWISE, }; - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const FanService = `${accessory.displayName} Fan`; - (this.Fan.Service = accessory.getService(this.hap.Service.Fanv2) - || accessory.addService(this.hap.Service.Fanv2)), FanService; - - this.Fan.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // handle on / off events using the Active characteristic - this.Fan.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - + this.Fan.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Fan.Active; + }) + .onSet(this.ActiveSet.bind(this)); + accessory.context.Fan.Name = this.Fan.Name; if (device.irfan?.rotation_speed) { // handle Rotation Speed events using the RotationSpeed characteristic @@ -62,6 +65,9 @@ export class IRFan extends irdeviceBase { minValue: Number(this.minValue(device)), maxValue: Number(this.maxValue(device)), }) + .onGet(() => { + return this.Fan.RotationSpeed; + }) .onSet(this.RotationSpeedSet.bind(this)); } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !device.irfan?.swing_mode) { const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed); @@ -76,7 +82,12 @@ export class IRFan extends irdeviceBase { if (device.irfan?.swing_mode) { // handle Osolcation events using the SwingMode characteristic - this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode).onSet(this.SwingModeSet.bind(this)); + this.Fan.Service + .getCharacteristic(this.hap.Characteristic.SwingMode) + .onGet(() => { + return this.Fan.SwingMode; + }) + .onSet(this.SwingModeSet.bind(this)); } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.SwingMode) && !device.irfan?.swing_mode) { const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode); this.Fan.Service.removeCharacteristic(characteristic); @@ -87,6 +98,7 @@ export class IRFan extends irdeviceBase { `Clear Cache on ${this.accessory.displayName} To Remove Chracteristic`, ); } + accessory.context.Fan.Name = this.Fan.Name; } async minStep(device: irdevice & irDevicesConfig): Promise { diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index ff71f3d5..2b398340 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -18,17 +18,20 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class Light extends irdeviceBase { // Services private LightBulb?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private ProgrammableSwitchOn?: { + Name: CharacteristicValue; Service: Service; ProgrammableSwitchEvent: CharacteristicValue; ProgrammableSwitchOutputState: CharacteristicValue; }; private ProgrammableSwitchOff?: { + Name: CharacteristicValue; Service: Service; ProgrammableSwitchEvent: CharacteristicValue; ProgrammableSwitchOutputState: CharacteristicValue; @@ -41,68 +44,93 @@ export class Light extends irdeviceBase { ) { super(platform, accessory, device); + + if (!device.irlight?.stateless) { + if (!accessory.context.LightBulb) { + accessory.context.LightBulb = {}; + } // Initialize LightBulb property this.LightBulb = { - Service: accessory.getService(this.hap.Service.Lightbulb)! as Service, + Name: accessory.context.LightBulb.Name ?? `${accessory.displayName} ${device.remoteType}`, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, }; - // get the Light service if it exists, otherwise create a new Light service - // you can create multiple services for each accessory - const LightBulbService = `${accessory.displayName} ${device.remoteType}`; - (this.LightBulb.Service = accessory.getService(this.hap.Service.Lightbulb) - || accessory.addService(this.hap.Service.Lightbulb)), LightBulbService; - - - this.LightBulb.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - // handle on / off events using the On characteristic - this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.LightBulb.Name = this.LightBulb.Name; } else { + // Initialize ProgrammableSwitchOn Service + if (!accessory.context.ProgrammableSwitchOn) { + accessory.context.ProgrammableSwitchOn = {}; + } + this.ProgrammableSwitchOn = { + Name: accessory.context.ProgrammableSwitchOn.Name ?? `${accessory.displayName} ${device.remoteType} On`, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, + ProgrammableSwitchEvent: accessory.context.ProgrammableSwitchEvent ?? this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, + ProgrammableSwitchOutputState: accessory.context.ProgrammableSwitchOutputState ?? 0, + }; - // create a new Stateful Programmable Switch On service - const ProgrammableSwitchOn = `${accessory.displayName} ${device.remoteType} On`; - (this.ProgrammableSwitchOn!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchOn; - - - this.ProgrammableSwitchOn?.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} On`); - - this.ProgrammableSwitchOn?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ - validValueRanges: [0, 0], - minValue: 0, - maxValue: 0, - validValues: [0], - }) + // Initialize ProgrammableSwitchOn Characteristics + this.ProgrammableSwitchOn?.Service + .setCharacteristic(this.hap.Characteristic.Name, this.ProgrammableSwitchOn.Name) + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ + validValueRanges: [0, 0], + minValue: 0, + maxValue: 0, + validValues: [0], + }) .onGet(() => { return this.ProgrammableSwitchOn!.ProgrammableSwitchEvent; }); - this.ProgrammableSwitchOn?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + this.ProgrammableSwitchOn?.Service + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + .onGet(() => { + return this.ProgrammableSwitchOn!.ProgrammableSwitchOutputState; + }) .onSet(this.ProgrammableSwitchOutputStateSetOn.bind(this)); + accessory.context.ProgrammableSwitchOn.Name = this.ProgrammableSwitchOn.Name; + // Initialize ProgrammableSwitchOff Service + if (!accessory.context.ProgrammableSwitchOff) { + accessory.context.ProgrammableSwitchOff = {}; + } + this.ProgrammableSwitchOff = { + Name: accessory.context.ProgrammableSwitchOff.Name ?? `${accessory.displayName} ${device.remoteType} Off`, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, + ProgrammableSwitchEvent: accessory.context.ProgrammableSwitchEvent ?? this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, + ProgrammableSwitchOutputState: accessory.context.ProgrammableSwitchOutputState ?? 0, + }; - - // create a new Stateful Programmable Switch Off service - const ProgrammableSwitchOff = `${accessory.displayName} ${device.remoteType} Off`; - (this.ProgrammableSwitchOff!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), ProgrammableSwitchOff; - - - this.ProgrammableSwitchOff?.Service.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Off`); - - this.ProgrammableSwitchOff?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ - validValueRanges: [0, 0], - minValue: 0, - maxValue: 0, - validValues: [0], - }) + // Initialize ProgrammableSwitchOff Characteristics + this.ProgrammableSwitchOff?.Service + .setCharacteristic(this.hap.Characteristic.Name, this.ProgrammableSwitchOff.Name) + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent).setProps({ + validValueRanges: [0, 0], + minValue: 0, + maxValue: 0, + validValues: [0], + }) .onGet(() => { return this.ProgrammableSwitchOff!.ProgrammableSwitchEvent; }); - this.ProgrammableSwitchOff?.Service.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + this.ProgrammableSwitchOff?.Service + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + .onGet(() => { + return this.ProgrammableSwitchOff!.ProgrammableSwitchOutputState; + }) .onSet(this.ProgrammableSwitchOutputStateSetOff.bind(this)); + accessory.context.ProgrammableSwitchOff.Name = this.ProgrammableSwitchOff.Name; } } diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 98568a4a..38e27d5f 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -18,51 +18,61 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class Others extends irdeviceBase { // Services private Switch?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private GarageDoor?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Door?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Window?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private WindowCovering?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; - private Lock?: { + private LockMechanism?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Faucet?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Fan?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private StatefulProgrammableSwitch?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; private Outlet?: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -82,11 +92,27 @@ export class Others extends irdeviceBase { // deviceType if (this.otherDeviceType === 'switch') { - // Initialize Switch property + // Initialize Switch Service + if (!accessory.context.Switch) { + accessory.context.Switch = {}; + } this.Switch = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Switch`, + Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Switch`); + + this.Switch.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Switch!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Switch.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -96,21 +122,29 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Switch Service - const SwitchService = `${accessory.displayName} Switch`; - (this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service - || accessory.addService(this.hap.Service.Switch)), SwitchService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Switch`); - - this.Switch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'garagedoor') { - // Initialize Garage Door property + // Initialize Garage Door Service + if (!accessory.context.GarageDoor) { + accessory.context.GarageDoor = {}; + } this.GarageDoor = { - Service: accessory.getService(this.hap.Service.GarageDoorOpener) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Garage Door`, + Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Garage Door Opener`); + + this.GarageDoor.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false) + .getCharacteristic(this.hap.Characteristic.TargetDoorState) + .onGet(() => { + return this.GarageDoor!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.GarageDoor.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -120,40 +154,21 @@ export class Others extends irdeviceBase { this.removeWindowService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add GarageDoor Service - const GarageDoorService = `${accessory.displayName} Garage Door Opener`; - (this.GarageDoor!.Service = accessory.getService(this.hap.Service.GarageDoorOpener) as Service - || accessory.addService(this.hap.Service.GarageDoorOpener)), GarageDoorService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Garage Door Opener`); - - this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.GarageDoor!.Service.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this)); - this.GarageDoor!.Service.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false); } else if (this.otherDeviceType === 'door') { - // Initialize Door property + // Initialize Door Service + if (!accessory.context.Door) { + accessory.context.Door = {}; + } this.Door = { - Service: accessory.getService(this.hap.Service.Door) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Door`, + Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service, + On: accessory.context.On ?? false, }; - this.removeFanService(accessory); - this.removeLockService(accessory); - this.removeOutletService(accessory); - this.removeFaucetService(accessory); - this.removeSwitchService(accessory); - this.removeWindowService(accessory); - this.removeGarageDoorService(accessory); - this.removeWindowCoveringService(accessory); - this.removeStatefulProgrammableSwitchService(accessory); - - // Add Door Service - const DoorService = `${accessory.displayName} Door`; - (this.Door!.Service = accessory.getService(this.hap.Service.Door) as Service - || accessory.addService(this.hap.Service.Door)), DoorService; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Door`); - this.Door!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); this.Door!.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -161,32 +176,36 @@ export class Others extends irdeviceBase { maxValue: 100, minStep: 100, }) + .onGet(() => { + return this.GarageDoor!.On; + }) .onSet(this.OnSet.bind(this)); - this.Door!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - } else if (this.otherDeviceType === 'window') { - // Initialize Window property - this.Window = { - Service: accessory.getService(this.hap.Service.Window) as Service, - On: accessory.context.On || false, - }; + accessory.context.Name = this.Door.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); - this.removeDoorService(accessory); this.removeOutletService(accessory); this.removeFaucetService(accessory); this.removeSwitchService(accessory); + this.removeWindowService(accessory); this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Window Service - const WindowService = `${accessory.displayName} Window`; - (this.Window!.Service = accessory.getService(this.hap.Service.Window) as Service - || accessory.addService(this.hap.Service.Window)), WindowService; + } else if (this.otherDeviceType === 'window') { + // Initialize Window Service + if (!accessory.context.Window) { + accessory.context.Window = {}; + } + this.Window = { + Name: accessory.context.Name ?? `${accessory.displayName} Window`, + Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service, + On: accessory.context.On ?? false, + }; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window`); - this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Window!.Service + this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -194,32 +213,36 @@ export class Others extends irdeviceBase { maxValue: 100, minStep: 100, }) + .onGet(() => { + return this.GarageDoor!.On; + }) .onSet(this.OnSet.bind(this)); - this.Window!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - } else if (this.otherDeviceType === 'windowcovering') { - // Initialize WindowCovering property - this.WindowCovering = { - Service: accessory.getService(this.hap.Service.WindowCovering) as Service, - On: accessory.context.On || false, - }; + accessory.context.Name = this.Window.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); this.removeOutletService(accessory); this.removeFaucetService(accessory); this.removeSwitchService(accessory); - this.removeWindowService(accessory); this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add WindowCovering Service - const WindowCoveringService = `${accessory.displayName} Window Covering`; - (this.WindowCovering!.Service = accessory.getService(this.hap.Service.WindowCovering) as Service - || accessory.addService(this.hap.Service.WindowCovering)), WindowCoveringService; + } else if (this.otherDeviceType === 'windowcovering') { + // Initialize WindowCovering Service + if (!accessory.context.WindowCovering) { + accessory.context.WindowCovering = {}; + } + this.WindowCovering = { + Name: accessory.context.Name ?? `${accessory.displayName} Window Covering`, + Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, + On: accessory.context.On ?? false, + }; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window Covering`); - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.WindowCovering!.Service + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); + this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -227,14 +250,45 @@ export class Others extends irdeviceBase { maxValue: 100, minStep: 100, }) + .onGet(() => { + return this.WindowCovering!.On; + }) .onSet(this.OnSet.bind(this)); - this.WindowCovering!.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + accessory.context.Name = this.WindowCovering.Name; + + // Remove other services + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeOutletService(accessory); + this.removeFaucetService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'lock') { - // Initialize Lock property - this.Lock = { - Service: accessory.getService(this.hap.Service.LockMechanism) as Service, - On: accessory.context.On || false, + // Initialize Lock Service + if (!accessory.context.LockMechanism) { + accessory.context.LockMechanism = {}; + } + this.LockMechanism = { + Name: accessory.context.Name ?? `${accessory.displayName} Lock`, + Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Lock`); + + this.LockMechanism.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.LockTargetState) + .onGet(() => { + return this.LockMechanism!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.LockMechanism.Name; + + // Remove other services this.removeFanService(accessory); this.removeDoorService(accessory); this.removeOutletService(accessory); @@ -244,21 +298,28 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Lock Service - const LockService = `${accessory.displayName} Lock`; - (this.Lock!.Service = accessory.getService(this.hap.Service.LockMechanism) as Service - || accessory.addService(this.hap.Service.LockMechanism)), LockService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Lock`); - - this.Lock!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Lock!.Service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'faucet') { - // Initialize Faucet property + // Initialize Faucet Service + if (!accessory.context.Faucet) { + accessory.context.Faucet = {}; + } this.Faucet = { - Service: accessory.getService(this.hap.Service.Faucet) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Faucet`, + Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Faucet`); + + this.Faucet.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Faucet!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Faucet.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -268,21 +329,28 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Faucet Service - const FaucetService = `${accessory.displayName} Faucet`; - (this.Faucet!.Service = accessory.getService(this.hap.Service.Faucet) as Service - || accessory.addService(this.hap.Service.Faucet)), FaucetService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Faucet`); - - this.Faucet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Faucet!.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'fan') { - // Initialize Fan property + // Initialize Fan Service + if (!accessory.context.Fan) { + accessory.context.Fan = {}; + } this.Fan = { - Service: accessory.getService(this.hap.Service.Fanv2) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Fan`, + Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Fan`); + + this.Fan.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Fan!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Fan.Name; + + // Remove other services this.removeLockService(accessory); this.removeDoorService(accessory); this.removeFaucetService(accessory); @@ -292,21 +360,30 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Fan Service - const FanService = `${accessory.displayName} Fan`; - (this.Fan!.Service = accessory.getService(this.hap.Service.Fanv2) as Service - || accessory.addService(this.hap.Service.Fanv2)), FanService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Fan`); - - this.Fan!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Fan!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } else if (this.otherDeviceType === 'stateful') { + // Initialize Lock Service + if (!accessory.context.StatefulProgrammableSwitch) { + accessory.context.StatefulProgrammableSwitch = {}; + } // Initialize StatefulProgrammableSwitch property this.StatefulProgrammableSwitch = { - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Stateful Programmable Switch`, + Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) + ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); + + this.StatefulProgrammableSwitch.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) + .onGet(() => { + return this.StatefulProgrammableSwitch!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.StatefulProgrammableSwitch.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -316,23 +393,28 @@ export class Others extends irdeviceBase { this.removeWindowService(accessory); this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); - - // Add StatefulProgrammableSwitch.Service - const StatefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`; - (this.StatefulProgrammableSwitch!.Service = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service - || accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), StatefulProgrammableSwitchService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); - - this.StatefulProgrammableSwitch!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.StatefulProgrammableSwitch!.Service - .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState) - .onSet(this.OnSet.bind(this)); } else { - // Initialize Outlet property + // Initialize Outlet Service + if (!accessory.context.Outlet) { + accessory.context.Outlet = {}; + } this.Outlet = { - Service: accessory.getService(this.hap.Service.Outlet) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} Outlet`, + Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, + On: accessory.context.On ?? false, }; + this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Outlet`); + + this.Outlet.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Outlet!.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Name = this.Outlet.Name; + + // Remove other services this.removeFanService(accessory); this.removeLockService(accessory); this.removeDoorService(accessory); @@ -342,15 +424,6 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); - - // Add Outlet.Service - const OutletService = `${accessory.displayName} Outlet`; - (this.Outlet!.Service = accessory.getService(this.hap.Service.Outlet) as Service - || accessory.addService(this.hap.Service.Outlet)), OutletService; - this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Outlet`); - - this.Outlet!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); } } @@ -394,11 +467,11 @@ export class Others extends irdeviceBase { } else if (this.otherDeviceType === 'lock') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); if (value === this.hap.Characteristic.LockTargetState.SECURED) { - this.Lock!.On = false; + this.LockMechanism!.On = false; } else { - this.Lock!.On = true; + this.LockMechanism!.On = true; } - On = this.Lock!.On; + On = this.LockMechanism!.On; } else if (this.otherDeviceType === 'faucet') { this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Set Active: ${value}`); if (value === this.hap.Characteristic.Active.INACTIVE) { @@ -589,22 +662,26 @@ export class Others extends irdeviceBase { } this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); } else if (this.otherDeviceType === 'lock') { - if (this.Lock!.On === undefined) { - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Lock!.On}`); + if (this.LockMechanism?.On === undefined) { + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.LockMechanism?.On}`); } else { - if (this.Lock!.On) { - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); + if (this.LockMechanism.On) { + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + this.hap.Characteristic.LockTargetState.UNSECURED); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.hap.Characteristic.LockCurrentState.UNSECURED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURE (${this.Lock!.On})`); + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURE (${this.LockMechanism.On})`); } else { - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.Lock!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + this.hap.Characteristic.LockTargetState.SECURED); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.hap.Characteristic.LockCurrentState.SECURED); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.Lock!.On})`); + + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.LockMechanism.On})`); } } - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Lock On: ${this.Lock!.On}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Lock On: ${this.LockMechanism?.On}`); } else if (this.otherDeviceType === 'faucet') { if (this.Faucet!.On === undefined) { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.Faucet!.On}`); @@ -730,10 +807,10 @@ export class Others extends irdeviceBase { async removeLockService(accessory: PlatformAccessory): Promise { // If Lock.Service still present, then remove first - if (this.Lock?.Service) { - this.Lock!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + if (this.LockMechanism?.Service) { + this.LockMechanism!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.Lock!.Service); + accessory.removeService(this.LockMechanism!.Service); } } diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 485f7946..b3e230c3 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -18,6 +18,8 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class TV extends irdeviceBase { // Services private Television: { + Name: CharacteristicValue; + ConfiguredName: CharacteristicValue; Service: Service; Active: CharacteristicValue; ActiveIdentifier: CharacteristicValue; @@ -26,6 +28,7 @@ export class TV extends irdeviceBase { }; private TelevisionSpeaker: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; VolumeControlType: CharacteristicValue; @@ -41,101 +44,89 @@ export class TV extends irdeviceBase { ) { super(platform, accessory, device); - // Initialize Television property + // Initialize Television Service + if (!accessory.context.Television) { + accessory.context.Television = {}; + } this.Television = { - Service: accessory.getService(this.hap.Service.Television) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, - ActiveIdentifier: accessory.context.ActiveIdentifier || 1, - SleepDiscoveryMode: accessory.context.SleepDiscoveryMode || this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, - RemoteKey: accessory.context.RemoteKey || this.hap.Characteristic.RemoteKey.EXIT, - }; - - // Initialize TelevisionSpeaker property - this.TelevisionSpeaker = { - Service: accessory.getService(this.hap.Service.TelevisionSpeaker) as Service, - Active: accessory.context.Active || false, - VolumeControlType: accessory.context.VolumeControlType || this.hap.Characteristic.VolumeControlType.ABSOLUTE, - VolumeSelector: accessory.context.VolumeSelector || this.hap.Characteristic.VolumeSelector.INCREMENT, + Name: accessory.context.Television.Name ?? `${accessory.displayName} ${device.remoteType}`, + ConfiguredName: accessory.context.Television.ConfiguredName ?? `${accessory.displayName} ${device.remoteType}`, + Service: accessory.getService(this.hap.Service.Television) ?? accessory.addService(this.hap.Service.Television) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, + ActiveIdentifier: accessory.context.ActiveIdentifier ?? 1, + SleepDiscoveryMode: accessory.context.SleepDiscoveryMode ?? this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, + RemoteKey: accessory.context.RemoteKey ?? this.hap.Characteristic.RemoteKey.EXIT, }; - // default placeholders - if (this.Television.Active === undefined) { - this.Television.Active = this.hap.Characteristic.Active.INACTIVE; - } else { - this.Television.Active = this.accessory.context.Active; - } - if (this.Television.ActiveIdentifier === undefined) { - this.Television.ActiveIdentifier = 1; - } else { - this.Television.ActiveIdentifier = this.accessory.context.ActiveIdentifier; - } - - // set the accessory category - const TelevisionServiceCategory = `${accessory.displayName} ${device.remoteType}`; switch (device.remoteType) { case 'Speaker': case 'DIY Speaker': accessory.category = this.platform.api.hap.Categories.SPEAKER; - (this.Television.Service = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; case 'IPTV': case 'DIY IPTV': accessory.category = this.platform.api.hap.Categories.TV_STREAMING_STICK; - (this.Television.Service = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; case 'DVD': case 'DIY DVD': case 'Set Top Box': case 'DIY Set Top Box': accessory.category = this.platform.api.hap.Categories.TV_SET_TOP_BOX; - (this.Television.Service = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; break; default: accessory.category = this.platform.api.hap.Categories.TELEVISION; - - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - (this.Television.Service = accessory.getService(this.hap.Service.Television) - || accessory.addService(this.hap.Service.Television)), TelevisionServiceCategory; } - this.Television.Service.getCharacteristic(this.hap.Characteristic.ConfiguredName); - - // set sleep discovery characteristic - this.Television.Service.setCharacteristic( - this.hap.Characteristic.SleepDiscoveryMode, - this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, - ); - - // handle on / off events using the Active characteristic - this.Television.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); - - this.Television.Service.setCharacteristic(this.hap.Characteristic.ActiveIdentifier, 1); - - // handle input source changes - this.Television.Service.getCharacteristic(this.hap.Characteristic.ActiveIdentifier).onSet(this.ActiveIdentifierSet.bind(this)); - - // handle remote control input - this.Television.Service.getCharacteristic(this.hap.Characteristic.RemoteKey).onSet(this.RemoteKeySet.bind(this)); - - /** - * Create a speaker service to allow volume control - */ - // create a new Television Speaker service - const TelevisionSpeakerService = `${accessory.displayName} Speaker`; - (this.TelevisionSpeaker.Service = accessory.getService(this.hap.Service.TelevisionSpeaker) - || accessory.addService(this.hap.Service.TelevisionSpeaker)), TelevisionSpeakerService; + this.Television.Service + .setCharacteristic(this.hap.Characteristic.SleepDiscoveryMode, this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, this.Television.ConfiguredName) + .getCharacteristic(this.hap.Characteristic.ConfiguredName); + + this.Television.Service + .setCharacteristic(this.hap.Characteristic.ActiveIdentifier, 1) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Television.Active; + }) + .onSet(this.ActiveSet.bind(this)); + + this.Television.Service + .getCharacteristic(this.hap.Characteristic.ActiveIdentifier) + .onGet(() => { + return this.Television.ActiveIdentifier; + }) + .onSet(this.ActiveIdentifierSet.bind(this)); + + this.Television.Service + .getCharacteristic(this.hap.Characteristic.RemoteKey) + .onGet(() => { + return this.Television.RemoteKey; + }) + .onSet(this.RemoteKeySet.bind(this)); + accessory.context.Television.Name = this.Television.Name; + + // Initialize TelevisionSpeaker Service + if (!accessory.context.TelevisionSpeaker) { + accessory.context.TelevisionSpeaker = {}; + } + this.TelevisionSpeaker = { + Name: accessory.context.TelevisionSpeaker.Name ?? `${accessory.displayName} ${device.remoteType} Speaker`, + Service: accessory.getService(this.hap.Service.TelevisionSpeaker) ?? accessory.addService(this.hap.Service.TelevisionSpeaker) as Service, + Active: accessory.context.Active ?? false, + VolumeControlType: accessory.context.VolumeControlType ?? this.hap.Characteristic.VolumeControlType.ABSOLUTE, + VolumeSelector: accessory.context.VolumeSelector ?? this.hap.Characteristic.VolumeSelector.INCREMENT, + }; - this.TelevisionSpeaker.Service.setCharacteristic(this.hap.Characteristic.Name, TelevisionSpeakerService); this.TelevisionSpeaker.Service + .setCharacteristic(this.hap.Characteristic.Name, this.TelevisionSpeaker.Name) .setCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE) - .setCharacteristic(this.hap.Characteristic.VolumeControlType, this.hap.Characteristic.VolumeControlType.ABSOLUTE); - - // handle volume control - this.TelevisionSpeaker.Service.getCharacteristic(this.hap.Characteristic.VolumeSelector).onSet(this.VolumeSelectorSet.bind(this)); + .setCharacteristic(this.hap.Characteristic.VolumeControlType, this.hap.Characteristic.VolumeControlType.ABSOLUTE) + .getCharacteristic(this.hap.Characteristic.VolumeSelector) + .onGet(() => { + return this.TelevisionSpeaker.VolumeSelector; + }) + .onSet(this.VolumeSelectorSet.bind(this)); + accessory.context.TelevisionSpeaker.Name = this.TelevisionSpeaker.Name; } async VolumeSelectorSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index ef2dca5a..fcb30596 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class VacuumCleaner extends irdeviceBase { // Services private Switch: { + Name: CharacteristicValue; Service: Service; On: CharacteristicValue; }; @@ -29,21 +30,24 @@ export class VacuumCleaner extends irdeviceBase { ) { super(platform, accessory, device); - // Initialize Switch property + // Initialize Switch Service + if (!accessory.context.Switch) { + accessory.context.Switch = {}; + } this.Switch = { - Service: accessory.getService(this.hap.Service.Switch) as Service, - On: accessory.context.On || false, + Name: accessory.context.Name ?? `${accessory.displayName} ${device.remoteType}`, + Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, + On: accessory.context.On ?? false, }; - // get the Switch service if it exists, otherwise create a new Switch service - const SwitchService = `${accessory.displayName} ${device.remoteType}`; - (this.Switch.Service = accessory.getService(this.hap.Service.Switch) - || accessory.addService(this.hap.Service.Switch)), SwitchService; - - this.Switch.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // handle on / off events using the On characteristic - this.Switch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)); + this.Switch.Service + .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.Switch.On; + }) + .onSet(this.OnSet.bind(this)); + accessory.context.Switch.Name = this.Switch.Name; } async OnSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index 3518bece..a7343846 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -18,6 +18,7 @@ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge export class WaterHeater extends irdeviceBase { // Services private Valve: { + Name: CharacteristicValue; Service: Service; Active: CharacteristicValue; }; @@ -29,25 +30,25 @@ export class WaterHeater extends irdeviceBase { ) { super(platform, accessory, device); - // Initialize Valve property + // Initialize Switch Service + if (!accessory.context.Valve) { + accessory.context.Valve = {}; + } this.Valve = { - Service: accessory.getService(this.hap.Service.Valve) as Service, - Active: accessory.context.Active || this.hap.Characteristic.Active.INACTIVE, + Name: accessory.context.Name ?? `${accessory.displayName} ${device.remoteType}`, + Service: accessory.getService(this.hap.Service.Valve) ?? accessory.addService(this.hap.Service.Valve) as Service, + Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, }; - // get the Television service if it exists, otherwise create a new Television service - // you can create multiple services for each accessory - const ValveService = `${accessory.displayName} ${device.remoteType}`; - (this.Valve.Service = accessory.getService(this.hap.Service.Valve) - || accessory.addService(this.hap.Service.Valve)), ValveService; - - this.Valve.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); - - // set sleep discovery characteristic - this.Valve.Service.setCharacteristic(this.hap.Characteristic.ValveType, this.hap.Characteristic.ValveType.GENERIC_VALVE); - - // handle on / off events using the Active characteristic - this.Valve.Service.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this)); + this.Valve.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ValveType, this.hap.Characteristic.ValveType.GENERIC_VALVE) + .getCharacteristic(this.hap.Characteristic.Active) + .onGet(() => { + return this.Valve.Active; + }) + .onSet(this.ActiveSet.bind(this)); + accessory.context.Valve.Name = this.Valve.Name; } async ActiveSet(value: CharacteristicValue): Promise { From 1d618defeb1d132791312a2cb085248627c9fc95 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 15:31:36 -0500 Subject: [PATCH 66/73] context --- src/device/blindtilt.ts | 13 ++--- src/device/bot.ts | 52 +++++++++++-------- src/device/ceilinglight.ts | 7 +-- src/device/colorbulb.ts | 3 +- src/device/contact.ts | 24 +++++---- src/device/device.ts | 22 ++++---- src/device/fan.ts | 12 +++-- src/device/hub.ts | 15 +++--- src/device/humidifier.ts | 10 ++-- src/device/iosensor.ts | 16 +++--- src/device/lightstrip.ts | 5 +- src/device/lock.ts | 20 ++++--- src/device/meter.ts | 13 +++-- src/device/meterplus.ts | 15 +++--- src/device/motion.ts | 14 +++-- src/device/plug.ts | 5 +- src/device/robotvacuumcleaner.ts | 10 ++-- src/device/waterdetector.ts | 6 ++- src/irdevice/airconditioner.ts | 11 ++-- src/irdevice/airpurifier.ts | 17 +++--- src/irdevice/camera.ts | 8 ++- src/irdevice/fan.ts | 10 ++-- src/irdevice/light.ts | 22 +++----- src/irdevice/other.ts | 89 ++++++++++++-------------------- src/irdevice/tv.ts | 12 ++--- src/irdevice/vacuumcleaner.ts | 8 ++- src/irdevice/waterheater.ts | 8 ++- 27 files changed, 222 insertions(+), 225 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 0bbc97c7..a367096b 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -66,8 +66,8 @@ export class BlindTilt extends deviceBase { this.blindTiltUpdateInProgress = false; this.setNewTarget = false; - accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; // Initialize WindowCovering Service + accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; this.WindowCovering = { Name: accessory.context.WindowCovering.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, @@ -119,7 +119,6 @@ export class BlindTilt extends deviceBase { return this.WindowCovering.TargetHorizontalTiltAngle; }) .onSet(this.TargetHorizontalTiltAngleSet.bind(this)); - accessory.context.WindowCovering.Name = this.WindowCovering.Name; // Initialize WindowCovering CurrentHorizontalTiltAngle Characteristic this.WindowCovering.Service @@ -134,19 +133,20 @@ export class BlindTilt extends deviceBase { }); // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Name Characteristic this.Battery.Service .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - accessory.context.BatteryName = this.Battery.Name; // Initialize LightSensor Service if (device.blindTilt?.hide_lightsensor) { @@ -154,11 +154,13 @@ export class BlindTilt extends deviceBase { this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); } else { + accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { - Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; + accessory.context.LightSensor = this.LightSensor as object; // Initialize LightSensor Characteristics this.LightSensor.Service @@ -168,7 +170,6 @@ export class BlindTilt extends deviceBase { .onGet(() => { return this.LightSensor?.CurrentAmbientLightLevel ?? 0.0001; }); - accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/bot.ts b/src/device/bot.ts index c8c03b13..106bb1d9 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -119,22 +119,24 @@ export class Bot extends deviceBase { BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name) .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery) .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE); - accessory.context.BatteryName = this.Battery.Name; // deviceType if (this.botDeviceType === 'switch') { // Initialize Switch Service + accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Switch.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; + accessory.context.Switch = this.Switch as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`); // Initialize Switch Characteristics @@ -146,7 +148,6 @@ export class Bot extends deviceBase { return this.Switch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Switch.Name; // Remove other services this.removeFanService(accessory); @@ -160,11 +161,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'garagedoor') { // Initialize GarageDoor Service + accessory.context.GarageDoor = accessory.context.GarageDoor ?? {}; this.GarageDoor = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.GarageDoor.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service, On: accessory.context.On ?? false, }; + accessory.context.GarageDoor = this.GarageDoor as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`); // Initialize GarageDoor Characteristics @@ -181,7 +184,6 @@ export class Bot extends deviceBase { return this.GarageDoor!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.GarageDoor.Name; // Remove other services this.removeFanService(accessory); @@ -195,11 +197,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'door') { // Initialize Door Service + accessory.context.Door = accessory.context.Door ?? {}; this.Door = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Door.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service, On: accessory.context.On ?? false, }; + accessory.context.Door = this.Door as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`); // Initialize Door Characteristics @@ -216,7 +220,6 @@ export class Bot extends deviceBase { return this.Door!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Door.Name; // Remove other services this.removeFanService(accessory); @@ -230,11 +233,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'window') { // Initialize Window Service + accessory.context.Window = accessory.context.Window ?? {}; this.Window = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Window.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service, On: accessory.context.On ?? false, }; + accessory.context.Window = this.Window as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`); // Initialize Window Characteristics @@ -251,7 +256,6 @@ export class Bot extends deviceBase { return this.Window!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Window.Name; // Remove other services this.removeFanService(accessory); @@ -265,11 +269,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'windowcovering') { // Initialize WindowCovering Service + accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; this.WindowCovering = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.WindowCovering.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, On: accessory.context.On ?? false, }; + accessory.context.WindowCovering = this.WindowCovering as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`); // Initialize WindowCovering Characteristics @@ -286,7 +292,6 @@ export class Bot extends deviceBase { return this.WindowCovering!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.WindowCovering.Name; // Remove other services this.removeFanService(accessory); @@ -300,11 +305,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'lock') { // Initialize Lock Service + accessory.context.LockMechanism = accessory.context.LockMechanism ?? {}; this.LockMechanism = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.LockMechanism.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, On: accessory.context.On ?? false, }; + accessory.context.LockMechanism = this.LockMechanism as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`); // Initialize Lock Characteristics @@ -315,7 +322,6 @@ export class Bot extends deviceBase { return this.LockMechanism!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.LockMechanism.Name; // Remove other services this.removeFanService(accessory); @@ -329,11 +335,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'faucet') { // Initialize Faucet Service + accessory.context.Faucet = accessory.context.Faucet ?? {}; this.Faucet = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Faucet.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service, On: accessory.context.On ?? false, }; + accessory.context.Faucet = this.Faucet as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`); // Initialize Faucet Characteristics @@ -344,7 +352,6 @@ export class Bot extends deviceBase { return this.Faucet!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Faucet.Name; // Remove other services this.removeFanService(accessory); @@ -358,11 +365,13 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'fan') { // Initialize Fan Service + accessory.context.Fan = accessory.context.Fan ?? {}; this.Fan = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Fan.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, On: accessory.context.On ?? false, }; + accessory.context.Fan = this.Fan as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`); // Initialize Fan Characteristics @@ -373,7 +382,6 @@ export class Bot extends deviceBase { return this.Fan!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Fan.Name; // Remove other services this.removeLockService(accessory); @@ -387,12 +395,14 @@ export class Bot extends deviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.botDeviceType === 'stateful') { // Initialize StatefulProgrammableSwitch Service + accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {}; this.StatefulProgrammableSwitch = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.StatefulProgrammableSwitch.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, On: accessory.context.On ?? false, }; + accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); // Initialize StatefulProgrammableSwitch Characteristics @@ -403,7 +413,6 @@ export class Bot extends deviceBase { return this.StatefulProgrammableSwitch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.StatefulProgrammableSwitch.Name; // Remove other services this.removeFanService(accessory); @@ -417,11 +426,13 @@ export class Bot extends deviceBase { this.removeWindowCoveringService(accessory); } else { // Initialize Switch property + accessory.context.Outlet = accessory.context.Outlet ?? {}; this.Outlet = { - Name: accessory.context.Name ?? accessory.displayName, + Name: accessory.context.Outlet.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, On: accessory.context.On ?? false, }; + accessory.context.Outlet = this.Outlet as object; this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`); // Initialize Outlet Characteristics @@ -432,7 +443,6 @@ export class Bot extends deviceBase { return this.Outlet!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Outlet.Name; // Remove other services this.removeFanService(accessory); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 077a3319..f7aae35c 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -51,9 +51,10 @@ export class CeilingLight extends deviceBase { this.doCeilingLightUpdate = new Subject(); this.ceilingLightUpdateInProgress = false; - // Initialize LightBulb property + // Initialize LightBulb Service + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; this.LightBulb = { - Name: accessory.context.LightBulbName ?? accessory.displayName, + Name: accessory.context.LightBul.bName ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -61,6 +62,7 @@ export class CeilingLight extends deviceBase { Brightness: accessory.context.Brightness ?? 0, ColorTemperature: accessory.context.ColorTemperature ?? 140, }; + accessory.context.LightBulb = this.LightBulb as object; // Adaptive Lighting if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) { @@ -141,7 +143,6 @@ export class CeilingLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulbName = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 0c0da33e..34a4b2b3 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -51,8 +51,8 @@ export class ColorBulb extends deviceBase { this.doColorBulbUpdate = new Subject(); this.colorBulbUpdateInProgress = false; - accessory.context.LightBulb = accessory.context.LightBulb ?? {}; // Initialize LightBulb property + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; this.LightBulb = { Name: accessory.context.LightBulb.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, @@ -140,7 +140,6 @@ export class ColorBulb extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/contact.ts b/src/device/contact.ts index 908523d2..564e9609 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -57,12 +57,14 @@ export class Contact extends deviceBase { this.doContactUpdate = new Subject(); this.contactUpdateInProgress = false; - // Initialize Contact Sensor property + // Initialize Contact Sensor Service + accessory.context.ContactSensor = accessory.context.ContactSensor ?? {}; this.ContactSensor = { - Name: accessory.context.ContactSensorName ?? accessory.displayName, + Name: accessory.context.ContactSensor.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.ContactSensor) ?? accessory.addService(this.hap.Service.ContactSensor) as Service, ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; + accessory.context.ContactSensor = this.ContactSensor as object; // Initialize ContactSensor Characteristics this.ContactSensor.Service @@ -72,15 +74,16 @@ export class Contact extends deviceBase { .onGet(() => { return this.ContactSensor.ContactSensorState; }); - accessory.context.ContactSensorName = this.ContactSensor.Name; - // Initialize Battery property + // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -97,7 +100,6 @@ export class Contact extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Initialize Motion Sensor Service if (this.device.contact?.hide_motionsensor) { @@ -105,11 +107,13 @@ export class Contact extends deviceBase { this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; accessory.removeService(this.MotionSensor!.Service); } else { + accessory.context.MotionSensor = accessory.context.MotionSensor ?? {}; this.MotionSensor = { - Name: accessory.context.MotionSensorName ?? `${accessory.displayName} Motion Sensor`, + Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected ?? false, }; + accessory.context.MotionSensor = this.MotionSensor as object; // Motion Sensor Characteristics this.MotionSensor.Service @@ -119,7 +123,6 @@ export class Contact extends deviceBase { .onGet(() => { return this.MotionSensor!.MotionDetected; }); - accessory.context.MotionSensorName = this.MotionSensor.Name; } // Initialize Light Sensor Service @@ -128,11 +131,13 @@ export class Contact extends deviceBase { this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); } else { + accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { - Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; + accessory.context.LightSensor = this.LightSensor as object; // Light Sensor Characteristics this.LightSensor.Service @@ -142,7 +147,6 @@ export class Contact extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/device.ts b/src/device/device.ts index 2613076b..94577466 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -549,19 +549,19 @@ export abstract class deviceBase { } else if (device.firmware === undefined && device.version === undefined && accessory.context.version === undefined) { device.version = this.platform.version; accessory.context.version = this.platform.version; - } else { + } else if (accessory.context.device.version) { + accessory.context.version = accessory.context.device.version.replace(/^V|-.*$/g, ''); + }else { accessory.context.version = device.version?.replace(/^V|-.*$/g, ''); } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${accessory.context.version}`); - if (accessory.context.version) { - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); - } - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Context: ${JSON.stringify(accessory.context)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${accessory.context.device.version}`); + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(accessory.context.version); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Context: ${JSON.stringify(accessory.context.device)}`); } async statusCode(statusCode: number): Promise { diff --git a/src/device/fan.ts b/src/device/fan.ts index 8f46d272..9e6d9e25 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -44,13 +44,15 @@ export class Fan extends deviceBase { this.plugUpdateInProgress = false; // Initialize Fan Service + accessory.context.Fan = accessory.context.Fan ?? {}; this.Fan = { - Name: accessory.context.FanName ?? accessory.displayName, + Name: accessory.context.Fan.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED, RotationSpeed: accessory.context.RotationSpeed ?? 0, }; + accessory.context.Fan = this.Fan as object; // Initialize Fan Service this.Fan.Service @@ -75,16 +77,17 @@ export class Fan extends deviceBase { return this.Fan.SwingMode; }) .onSet(this.SwingModeSet.bind(this)); - accessory.context.FanName = this.Fan.Name; - // Initialize Battery property + // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? accessory.displayName, + Name: accessory.context.Battery.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Service this.Battery.Service @@ -107,7 +110,6 @@ export class Fan extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/hub.ts b/src/device/hub.ts index 43422e73..27d95694 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -52,11 +52,13 @@ export class Hub extends deviceBase { this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); } else { + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { - Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 0, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; // Initialize Temperature Sensor Characteristic this.TemperatureSensor.Service @@ -72,7 +74,6 @@ export class Hub extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service @@ -81,11 +82,13 @@ export class Hub extends deviceBase { this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); } else { + accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { - Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 0, }; + accessory.context.HumiditySensor = this.HumiditySensor as object; // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service @@ -97,7 +100,6 @@ export class Hub extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Initialize Light Sensor Service @@ -106,11 +108,13 @@ export class Hub extends deviceBase { this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); } else { + accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { - Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; + accessory.context.LightSensor = this.LightSensor as object; // Initialize Light Sensor Characteristics this.LightSensor!.Service @@ -122,7 +126,6 @@ export class Hub extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensorName = this.LightSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 14bdb3cf..a4a5ddec 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -48,8 +48,9 @@ export class Humidifier extends deviceBase { this.humidifierUpdateInProgress = false; // Initialize the HumidifierDehumidifier Service + accessory.context.HumidifierDehumidifier = accessory.context.HumidifierDehumidifier ?? {}; this.HumidifierDehumidifier = { - Name: accessory.context.HumidifierDehumidifierName ?? accessory.displayName, + Name: accessory.context.HumidifierDehumidifier.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.HumidifierDehumidifier) ?? accessory.addService(this.hap.Service.HumidifierDehumidifier) as Service, Active: accessory.context.Active ?? this.hap.Characteristic.Active.ACTIVE, @@ -61,6 +62,7 @@ export class Humidifier extends deviceBase { ?? this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE, RelativeHumidityHumidifierThreshold: accessory.context.RelativeHumidityHumidifierThreshold ?? 50, }; + accessory.context.HumidifierDehumidifier = this.HumidifierDehumidifier as object; // Initialize the HumidifierDehumidifier Characteristics this.HumidifierDehumidifier.Service @@ -98,7 +100,6 @@ export class Humidifier extends deviceBase { return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; }) .onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this)); - accessory.context.HumidifierDehumidifierName = this.HumidifierDehumidifier.Name; // Initialize the Temperature Sensor Service if (device.humidifier?.hide_temperature) { @@ -106,11 +107,13 @@ export class Humidifier extends deviceBase { this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); } else { + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { - Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 30, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; // Initialize the Temperature Sensor Characteristics this.TemperatureSensor.Service @@ -125,7 +128,6 @@ export class Humidifier extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 0c97a4c6..b711bdee 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -52,13 +52,15 @@ export class IOSensor extends deviceBase { this.doIOSensorUpdate = new Subject(); this.ioSensorUpdateInProgress = false; - // Initialize Battery property + // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -82,11 +84,13 @@ export class IOSensor extends deviceBase { this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); } else { + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { - Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; // Initialize Temperature Sensor Characteristics this.TemperatureSensor.Service @@ -102,7 +106,6 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature; }); - accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service @@ -111,11 +114,13 @@ export class IOSensor extends deviceBase { this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); } else { + accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { - Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; + accessory.context.HumiditySensor = this.HumiditySensor as object; // Initialize Humidity Sensor Characteristics this.HumiditySensor.Service @@ -127,7 +132,6 @@ export class IOSensor extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity; }); - accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index e7ad7ae5..af5e9193 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -46,8 +46,9 @@ export class StripLight extends deviceBase { this.stripLightUpdateInProgress = false; // Initialize the LightBulb Service + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; this.LightBulb = { - Name: accessory.context.LightBulbName ?? accessory.displayName, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Hue: accessory.context.Hue ?? 0, @@ -55,6 +56,7 @@ export class StripLight extends deviceBase { Brightness: accessory.context.Brightness ?? 0, ColorTemperature: accessory.context.ColorTemperature ?? 140, }; + accessory.context.LightBulb = this.LightBulb as object; // Adaptive Lighting if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) { @@ -137,7 +139,6 @@ export class StripLight extends deviceBase { return this.LightBulb.Saturation; }) .onSet(this.SaturationSet.bind(this)); - accessory.context.LightBulbName = this.LightBulb.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/lock.ts b/src/device/lock.ts index a0ffc7d2..4346c24c 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -51,12 +51,14 @@ export class Lock extends deviceBase { this.lockUpdateInProgress = false; // Initialize LockMechanism Service + accessory.context.LockMechanism = accessory.context.LockMechanism ?? {}; this.LockMechanism = { - Name: accessory.context.LockMechanismName ?? accessory.displayName, + Name: accessory.context.LockMechanism.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, LockTargetState: accessory.context.LockTargetState ?? this.hap.Characteristic.LockTargetState.SECURED, LockCurrentState: accessory.context.LockCurrentState ?? this.hap.Characteristic.LockCurrentState.SECURED, }; + accessory.context.LockMechanism = this.LockMechanism as object; // Initialize LockMechanism Characteristics this.LockMechanism.Service @@ -66,15 +68,16 @@ export class Lock extends deviceBase { return this.LockMechanism.LockTargetState; }) .onSet(this.LockTargetStateSet.bind(this)); - accessory.context.LockMechanismName = this.LockMechanism.Name; // Initialize Battery property + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -90,7 +93,6 @@ export class Lock extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Contact Sensor Service if (device.lock?.hide_contactsensor) { @@ -98,11 +100,13 @@ export class Lock extends deviceBase { this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; accessory.removeService(this.ContactSensor!.Service); } else { + accessory.context.ContactSensor = accessory.context.ContactSensor ?? {}; this.ContactSensor = { - Name: accessory.context.ContactSensorName ?? `${accessory.displayName} Contact Sensor`, + Name: accessory.context.ContactSensor.Name ?? `${accessory.displayName} Contact Sensor`, Service: accessory.getService(this.hap.Service.ContactSensor) ?? this.accessory.addService(this.hap.Service.ContactSensor) as Service, ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED, }; + accessory.context.ContactSensor = this.ContactSensor as object; // Initialize Contact Sensor Characteristics this.ContactSensor.Service @@ -112,7 +116,6 @@ export class Lock extends deviceBase { .onGet(() => { return this.ContactSensor!.ContactSensorState; }); - accessory.context.ContactSensorName = this.ContactSensor.Name; } // Initialize Latch Button Service @@ -121,11 +124,13 @@ export class Lock extends deviceBase { this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service; accessory.removeService(this.Switch!.Service); } else { + accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { - Name: accessory.context.SwitchName ?? `${accessory.displayName} Latch`, + Name: accessory.context.Switch.Name ?? `${accessory.displayName} Latch`, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; + accessory.context.Switch = this.Switch as object; // Initialize Latch Button Characteristics this.Switch.Service @@ -135,7 +140,6 @@ export class Lock extends deviceBase { return this.Switch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.SwitchName = this.Switch.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/meter.ts b/src/device/meter.ts index a5e3490b..45eff4ea 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -48,12 +48,14 @@ export class Meter extends deviceBase { this.meterUpdateInProgress = false; // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -69,7 +71,6 @@ export class Meter extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { @@ -77,11 +78,13 @@ export class Meter extends deviceBase { this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); } else { + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { - Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; // Initialize Temperature Sensor Characteristics this.TemperatureSensor.Service @@ -97,7 +100,6 @@ export class Meter extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { @@ -105,11 +107,13 @@ export class Meter extends deviceBase { this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); } else { + accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; + accessory.context.HumiditySensor = this.HumiditySensor as object; // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service @@ -121,7 +125,6 @@ export class Meter extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 1b121aa0..73b26dc9 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -52,12 +52,14 @@ export class MeterPlus extends deviceBase { this.meterUpdateInProgress = false; // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -73,7 +75,6 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { @@ -81,11 +82,13 @@ export class MeterPlus extends deviceBase { this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; accessory.removeService(this.TemperatureSensor!.Service); } else { + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { - Name: accessory.context.TemperatureSensorName ?? `${accessory.displayName} Temperature Sensor`, + Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature ?? 30, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; // Initialize Temperature Sensor Characteristics this.TemperatureSensor.Service @@ -101,7 +104,6 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.TemperatureSensor!.CurrentTemperature!; }); - accessory.context.TemperatureSensorName = this.TemperatureSensor.Name; } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { @@ -109,11 +111,13 @@ export class MeterPlus extends deviceBase { this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; accessory.removeService(this.HumiditySensor!.Service); } else { + accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { - Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`, + Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`, Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50, }; + accessory.context.HumiditySensor = this.HumiditySensor as object; // Initialize Humidity Sensor Characteristics this.HumiditySensor!.Service @@ -125,7 +129,6 @@ export class MeterPlus extends deviceBase { .onGet(() => { return this.HumiditySensor!.CurrentRelativeHumidity!; }); - accessory.context.HumiditySensorName = this.HumiditySensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/device/motion.ts b/src/device/motion.ts index 0898f71e..46ff5561 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -52,11 +52,13 @@ export class Motion extends deviceBase { this.motionUbpdateInProgress = false; // Initialize Motion Sensor property + accessory.context.MotionSensor = accessory.context.MotionSensor ?? {}; this.MotionSensor = { - Name: accessory.context.MotionSensorName ?? `${accessory.displayName} Motion Sensor`, + Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`, Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service, MotionDetected: accessory.context.MotionDetected ?? false, }; + accessory.context.MotionSensor = this.MotionSensor as object; // Initialize Motion Sensor Characteristics this.MotionSensor.Service @@ -69,12 +71,14 @@ export class Motion extends deviceBase { accessory.context.MotionSensorName = this.MotionSensor.Name; // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -91,7 +95,6 @@ export class Motion extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.BatteryName = this.Battery.Name; // Initialize Light Sensor Service if (device.motion?.hide_lightsensor) { @@ -99,11 +102,13 @@ export class Motion extends deviceBase { this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; accessory.removeService(this.LightSensor!.Service); } else { + accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { - Name: accessory.context.LightSensorName ?? `${accessory.displayName} Light Sensor`, + Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`, Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service, CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001, }; + accessory.context.LightSensor = this.LightSensor as object; // Initialize LightSensor Characteristics this.LightSensor!.Service @@ -113,7 +118,6 @@ export class Motion extends deviceBase { .onGet(() => { return this.LightSensor!.CurrentAmbientLightLevel; }); - accessory.context.LightSensorName = this.LightSensor.Name; }; diff --git a/src/device/plug.ts b/src/device/plug.ts index 32a8efd4..78680176 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -33,11 +33,13 @@ export class Plug extends deviceBase { this.plugUpdateInProgress = false; // Initialize Outlet Service + accessory.context.Outlet = accessory.context.Outlet ?? {}; this.Outlet = { - Name: accessory.context.OutletName ?? accessory.displayName, + Name: accessory.context.Outlet.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, On: accessory.context.On || false, }; + accessory.context.Outlet = this.Outlet as object; // Initialize Outlet Characteristics this.Outlet.Service @@ -47,7 +49,6 @@ export class Plug extends deviceBase { return this.Outlet.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.OutletName = this.Outlet.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 573c6884..3dd8aad1 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -44,12 +44,14 @@ export class RobotVacuumCleaner extends deviceBase { this.robotVacuumCleanerUpdateInProgress = false; // Initialize Lightbulb Service + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; this.LightBulb = { - Name: accessory.context.LightBulbName ?? accessory.displayName, + Name: accessory.context.LightBulb.Name ?? accessory.displayName, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On ?? false, Brightness: accessory.context.Brightness ?? 0, }; + accessory.context.LightBulb = this.LightBulb as object; // Initialize LightBulb Characteristics this.LightBulb.Service @@ -74,16 +76,17 @@ export class RobotVacuumCleaner extends deviceBase { return this.LightBulb.Brightness; }) .onSet(this.BrightnessSet.bind(this)); - accessory.context.LightBulbName = this.LightBulb.Name; // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { - Name: accessory.context.BatteryName ?? `${accessory.displayName} Battery`, + Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, BatteryLevel: accessory.context.BatteryLevel ?? 100, StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristics this.Battery.Service @@ -104,7 +107,6 @@ export class RobotVacuumCleaner extends deviceBase { .onGet(() => { return this.Battery.ChargingState; }); - accessory.context.BatteryName = this.Battery.Name; // Retrieve initial values and updateHomekit this.refreshStatus(); diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index dfe19310..e9c72904 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -51,6 +51,7 @@ export class WaterDetector extends deviceBase { this.WaterDetectorUpdateInProgress = false; // Initialize Battery Service + accessory.context.Battery = accessory.context.Battery ?? {}; this.Battery = { Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`, Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service, @@ -58,6 +59,7 @@ export class WaterDetector extends deviceBase { StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGEABLE, }; + accessory.context.Battery = this.Battery as object; // Initialize Battery Characteristic this.Battery.Service @@ -73,7 +75,6 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.Battery.StatusLowBattery; }); - accessory.context.Battery.Name = this.Battery.Name; // Initialize Leak Sensor Service if (device.waterdetector?.hide_leak) { @@ -81,12 +82,14 @@ export class WaterDetector extends deviceBase { this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; accessory.removeService(this.LeakSensor!.Service); } else { + accessory.context.LeakSensor = accessory.context.LeakSensor ?? {}; this.LeakSensor = { Name: accessory.context.LeakSensor.Name ?? `${accessory.displayName} Leak Sensor`, Service: accessory.getService(this.hap.Service.LeakSensor) ?? this.accessory.addService(this.hap.Service.LeakSensor) as Service, StatusActive: accessory.context.StatusActive ?? false, LeakDetected: accessory.context.LeakDetected ?? this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED, }; + accessory.context.LeakSensor = this.LeakSensor as object; // Initialize LeakSensor Characteristic this.LeakSensor!.Service @@ -96,7 +99,6 @@ export class WaterDetector extends deviceBase { .onGet(() => { return this.LeakSensor!.LeakDetected; }); - accessory.context.LeakSensor.Name = this.LeakSensor.Name; } // Retrieve initial values and updateHomekit diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index 33aa5d6f..9013efc1 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -64,9 +64,7 @@ export class AirConditioner extends irdeviceBase { // Initialize HeaterCooler Service - if (!accessory.context.HeaterCooler) { - accessory.context.HeaterCooler = {}; - } + accessory.context.HeaterCooler = accessory.context.HeaterCooler ?? {}; this.HeaterCooler = { Name: accessory.context.HeaterCooler.Name ?? `${accessory.displayName} ${device.remoteType}`, Service: accessory.getService(this.hap.Service.HeaterCooler) ?? accessory.addService(this.hap.Service.HeaterCooler) as Service, @@ -77,7 +75,7 @@ export class AirConditioner extends irdeviceBase { ThresholdTemperature: accessory.context.ThresholdTemperature ?? 24, RotationSpeed: accessory.context.RotationSpeed ?? 4, }; - + accessory.context.HeaterCooler = this.HeaterCooler as object; this.HeaterCooler.Service .setCharacteristic(this.hap.Characteristic.Name, this.HeaterCooler.Name) @@ -145,18 +143,19 @@ export class AirConditioner extends irdeviceBase { return await this.RotationSpeedGet(); }) .onSet(this.RotationSpeedSet.bind(this)); - accessory.context.HeaterCooler.Name = this.HeaterCooler.Name; // Initialize HumiditySensor property if (this.device.irair?.meterType && this.device.irair?.meterId) { const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`); this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); + accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { - Name: this.meter!.displayName, + Name: accessory.context.HumiditySensor ?? this.meter!.displayName, Service: this.meter!.getService(this.hap.Service.HumiditySensor) ?? this.meter!.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, }; + accessory.context.HumiditySensor = this.HumiditySensor as object; } if (this.device.irair?.meterType && this.device.irair?.meterId) { diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index dd2b1f19..181d55a8 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -56,11 +56,8 @@ export class AirPurifier extends irdeviceBase { ) { super(platform, accessory, device); - if (!accessory.context.AirPurifier) { - accessory.context.AirPurifier = {}; - } - - // Initialize AirPurifier property + // Initialize AirPurifier Service + accessory.context.AirPurifier = accessory.context.AirPurifier ?? {}; this.AirPurifier = { Name: accessory.context.AirPurifier.Name ?? `${accessory.displayName} Air Purifier`, Service: accessory.getService(this.hap.Service.AirPurifier) ?? accessory.addService(this.hap.Service.AirPurifier) as Service, @@ -69,6 +66,7 @@ export class AirPurifier extends irdeviceBase { CurrentAirPurifierState: accessory.context.CurrentAirPurifierState ?? this.hap.Characteristic.CurrentAirPurifierState.INACTIVE, TargetAirPurifierState: accessory.context.TargetAirPurifierState ?? this.hap.Characteristic.TargetAirPurifierState.AUTO, }; + accessory.context.AirPurifier = this.AirPurifier as object; this.AirPurifier.Service .setCharacteristic(this.hap.Characteristic.Name, this.AirPurifier.Name) @@ -90,17 +88,15 @@ export class AirPurifier extends irdeviceBase { return this.AirPurifier.TargetAirPurifierState; }) .onSet(this.TargetAirPurifierStateSet.bind(this)); - accessory.context.AirPurifier.Name = this.AirPurifier.Name; - if (!accessory.context.TemperatureSensor) { - accessory.context.TemperatureSensor = {}; - } - // Initialize TemperatureSensor property + // Initialize TemperatureSensor Service + accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`, Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? accessory.addService(this.hap.Service.TemperatureSensor) as Service, CurrentTemperature: accessory.context.CurrentTemperature || 24, }; + accessory.context.TemperatureSensor = this.TemperatureSensor as object; this.TemperatureSensor.Service .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name) @@ -108,7 +104,6 @@ export class AirPurifier extends irdeviceBase { .onGet(() => { return this.TemperatureSensor.CurrentTemperature; }); - accessory.context.TemperatureSensor.Name = this.TemperatureSensor.Name; } async ActiveSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index eb5f7620..b30fbc0b 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -30,15 +30,14 @@ export class Camera extends irdeviceBase { ) { super(platform, accessory, device); - if (!accessory.context.Switch) { - accessory.context.Switch = {}; - } - // Initialize Switch property + // Initialize Switch Service + accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { Name: accessory.context.Switch.Name ?? `${accessory.displayName} Camera`, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; + accessory.context.Switch = this.Switch as object; this.Switch.Service .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) @@ -47,7 +46,6 @@ export class Camera extends irdeviceBase { return this.Switch.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Switch.Name = this.Switch.Name; } async OnSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 8e81d26a..5153e43a 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -33,11 +33,8 @@ export class IRFan extends irdeviceBase { ) { super(platform, accessory, device); - if (!accessory.context.Fan) { - accessory.context.Fan = {}; - } - - // Initialize Switch property + // Initialize Switch Service + accessory.context.Fan = accessory.context.Fan ?? {}; this.Fan = { Name: accessory.context.Fan.Name ?? `${accessory.displayName} Fan`, Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, @@ -46,6 +43,7 @@ export class IRFan extends irdeviceBase { RotationSpeed: accessory.context.RotationSpeed ?? 0, RotationDirection: accessory.context.RotationDirection ?? this.hap.Characteristic.RotationDirection.CLOCKWISE, }; + accessory.context.Fan = this.Fan as object; this.Fan.Service .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name) @@ -54,7 +52,6 @@ export class IRFan extends irdeviceBase { return this.Fan.Active; }) .onSet(this.ActiveSet.bind(this)); - accessory.context.Fan.Name = this.Fan.Name; if (device.irfan?.rotation_speed) { // handle Rotation Speed events using the RotationSpeed characteristic @@ -98,7 +95,6 @@ export class IRFan extends irdeviceBase { `Clear Cache on ${this.accessory.displayName} To Remove Chracteristic`, ); } - accessory.context.Fan.Name = this.Fan.Name; } async minStep(device: irdevice & irDevicesConfig): Promise { diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 2b398340..2b5fc2b5 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -44,18 +44,15 @@ export class Light extends irdeviceBase { ) { super(platform, accessory, device); - - if (!device.irlight?.stateless) { - if (!accessory.context.LightBulb) { - accessory.context.LightBulb = {}; - } - // Initialize LightBulb property + // Initialize LightBulb Service + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; this.LightBulb = { Name: accessory.context.LightBulb.Name ?? `${accessory.displayName} ${device.remoteType}`, Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, On: accessory.context.On || false, }; + accessory.context.LightBulb = this.LightBulb as object; this.LightBulb.Service .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) @@ -64,12 +61,9 @@ export class Light extends irdeviceBase { return this.LightBulb!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.LightBulb.Name = this.LightBulb.Name; } else { // Initialize ProgrammableSwitchOn Service - if (!accessory.context.ProgrammableSwitchOn) { - accessory.context.ProgrammableSwitchOn = {}; - } + accessory.context.ProgrammableSwitchOn = accessory.context.ProgrammableSwitchOn ?? {}; this.ProgrammableSwitchOn = { Name: accessory.context.ProgrammableSwitchOn.Name ?? `${accessory.displayName} ${device.remoteType} On`, Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) @@ -77,6 +71,7 @@ export class Light extends irdeviceBase { ProgrammableSwitchEvent: accessory.context.ProgrammableSwitchEvent ?? this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, ProgrammableSwitchOutputState: accessory.context.ProgrammableSwitchOutputState ?? 0, }; + accessory.context.ProgrammableSwitchOn = this.ProgrammableSwitchOn as object; // Initialize ProgrammableSwitchOn Characteristics this.ProgrammableSwitchOn?.Service @@ -97,12 +92,9 @@ export class Light extends irdeviceBase { return this.ProgrammableSwitchOn!.ProgrammableSwitchOutputState; }) .onSet(this.ProgrammableSwitchOutputStateSetOn.bind(this)); - accessory.context.ProgrammableSwitchOn.Name = this.ProgrammableSwitchOn.Name; // Initialize ProgrammableSwitchOff Service - if (!accessory.context.ProgrammableSwitchOff) { - accessory.context.ProgrammableSwitchOff = {}; - } + accessory.context.ProgrammableSwitchOff = accessory.context.ProgrammableSwitchOff ?? {}; this.ProgrammableSwitchOff = { Name: accessory.context.ProgrammableSwitchOff.Name ?? `${accessory.displayName} ${device.remoteType} Off`, Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) @@ -110,6 +102,7 @@ export class Light extends irdeviceBase { ProgrammableSwitchEvent: accessory.context.ProgrammableSwitchEvent ?? this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, ProgrammableSwitchOutputState: accessory.context.ProgrammableSwitchOutputState ?? 0, }; + accessory.context.ProgrammableSwitchOff = this.ProgrammableSwitchOff as object; // Initialize ProgrammableSwitchOff Characteristics this.ProgrammableSwitchOff?.Service @@ -130,7 +123,6 @@ export class Light extends irdeviceBase { return this.ProgrammableSwitchOff!.ProgrammableSwitchOutputState; }) .onSet(this.ProgrammableSwitchOutputStateSetOff.bind(this)); - accessory.context.ProgrammableSwitchOff.Name = this.ProgrammableSwitchOff.Name; } } diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index 38e27d5f..dfd00544 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -93,14 +93,13 @@ export class Others extends irdeviceBase { // deviceType if (this.otherDeviceType === 'switch') { // Initialize Switch Service - if (!accessory.context.Switch) { - accessory.context.Switch = {}; - } + accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { - Name: accessory.context.Name ?? `${accessory.displayName} Switch`, + Name: accessory.context.Switch.Name ?? `${accessory.displayName} Switch`, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; + accessory.context.Switch = this.Switch as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Switch`); this.Switch.Service @@ -110,7 +109,6 @@ export class Others extends irdeviceBase { return this.Switch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Switch.Name; // Remove other services this.removeFanService(accessory); @@ -124,14 +122,13 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'garagedoor') { // Initialize Garage Door Service - if (!accessory.context.GarageDoor) { - accessory.context.GarageDoor = {}; - } + accessory.context.GarageDoor = accessory.context.GarageDoor ?? {}; this.GarageDoor = { - Name: accessory.context.Name ?? `${accessory.displayName} Garage Door`, + Name: accessory.context.GarageDoor.Name ?? `${accessory.displayName} Garage Door`, Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service, On: accessory.context.On ?? false, }; + accessory.context.GarageDoor = this.GarageDoor as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Garage Door Opener`); this.GarageDoor.Service @@ -142,7 +139,6 @@ export class Others extends irdeviceBase { return this.GarageDoor!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.GarageDoor.Name; // Remove other services this.removeFanService(accessory); @@ -156,14 +152,13 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'door') { // Initialize Door Service - if (!accessory.context.Door) { - accessory.context.Door = {}; - } + accessory.context.Door = accessory.context.Door ?? {}; this.Door = { - Name: accessory.context.Name ?? `${accessory.displayName} Door`, + Name: accessory.context.Door.Name ?? `${accessory.displayName} Door`, Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service, On: accessory.context.On ?? false, }; + accessory.context.Door = this.Door as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Door`); this.Door!.Service @@ -180,7 +175,6 @@ export class Others extends irdeviceBase { return this.GarageDoor!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Door.Name; // Remove other services this.removeFanService(accessory); @@ -194,14 +188,13 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'window') { // Initialize Window Service - if (!accessory.context.Window) { - accessory.context.Window = {}; - } + accessory.context.Window = accessory.context.Window ?? {}; this.Window = { - Name: accessory.context.Name ?? `${accessory.displayName} Window`, + Name: accessory.context.Window.Name ?? `${accessory.displayName} Window`, Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service, On: accessory.context.On ?? false, }; + accessory.context.Window = this.Window as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window`); this.Window!.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) @@ -217,7 +210,6 @@ export class Others extends irdeviceBase { return this.GarageDoor!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Window.Name; // Remove other services this.removeFanService(accessory); @@ -231,18 +223,18 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'windowcovering') { // Initialize WindowCovering Service - if (!accessory.context.WindowCovering) { - accessory.context.WindowCovering = {}; - } + accessory.context.WindowCovering = accessory.context.WindowCovering ?? {}; this.WindowCovering = { - Name: accessory.context.Name ?? `${accessory.displayName} Window Covering`, + Name: accessory.context.WindowCovering.Name ?? `${accessory.displayName} Window Covering`, Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, On: accessory.context.On ?? false, }; + accessory.context.WindowCovering = this.WindowCovering as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Window Covering`); - this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName); this.WindowCovering.Service + .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) + .setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ validValues: [0, 100], @@ -254,8 +246,6 @@ export class Others extends irdeviceBase { return this.WindowCovering!.On; }) .onSet(this.OnSet.bind(this)); - this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); - accessory.context.Name = this.WindowCovering.Name; // Remove other services this.removeFanService(accessory); @@ -268,15 +258,14 @@ export class Others extends irdeviceBase { this.removeGarageDoorService(accessory); this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'lock') { - // Initialize Lock Service - if (!accessory.context.LockMechanism) { - accessory.context.LockMechanism = {}; - } + // Initialize LockMechanism Service + accessory.context.LockMechanism = accessory.context.LockMechanism ?? {}; this.LockMechanism = { - Name: accessory.context.Name ?? `${accessory.displayName} Lock`, + Name: accessory.context.LockMechanism.Name ?? `${accessory.displayName} Lock`, Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, On: accessory.context.On ?? false, }; + accessory.context.LockMechanism = this.LockMechanism as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Lock`); this.LockMechanism.Service @@ -286,7 +275,6 @@ export class Others extends irdeviceBase { return this.LockMechanism!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.LockMechanism.Name; // Remove other services this.removeFanService(accessory); @@ -300,14 +288,13 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'faucet') { // Initialize Faucet Service - if (!accessory.context.Faucet) { - accessory.context.Faucet = {}; - } + accessory.context.Faucet = accessory.context.Faucet ?? {}; this.Faucet = { - Name: accessory.context.Name ?? `${accessory.displayName} Faucet`, + Name: accessory.context.Faucet.Name ?? `${accessory.displayName} Faucet`, Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service, On: accessory.context.On ?? false, }; + accessory.context.Faucet = this.Faucet as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Faucet`); this.Faucet.Service @@ -317,7 +304,6 @@ export class Others extends irdeviceBase { return this.Faucet!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Faucet.Name; // Remove other services this.removeFanService(accessory); @@ -331,14 +317,13 @@ export class Others extends irdeviceBase { this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'fan') { // Initialize Fan Service - if (!accessory.context.Fan) { - accessory.context.Fan = {}; - } + accessory.context.Fan = accessory.context.Fan ?? {}; this.Fan = { - Name: accessory.context.Name ?? `${accessory.displayName} Fan`, + Name: accessory.context.Fan.Name ?? `${accessory.displayName} Fan`, Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, On: accessory.context.On ?? false, }; + accessory.context.Fan = this.Fan as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Fan`); this.Fan.Service @@ -348,7 +333,6 @@ export class Others extends irdeviceBase { return this.Fan!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Fan.Name; // Remove other services this.removeLockService(accessory); @@ -361,17 +345,15 @@ export class Others extends irdeviceBase { this.removeWindowCoveringService(accessory); this.removeStatefulProgrammableSwitchService(accessory); } else if (this.otherDeviceType === 'stateful') { - // Initialize Lock Service - if (!accessory.context.StatefulProgrammableSwitch) { - accessory.context.StatefulProgrammableSwitch = {}; - } - // Initialize StatefulProgrammableSwitch property + // Initialize StatefulProgrammableSwitch Service + accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {}; this.StatefulProgrammableSwitch = { - Name: accessory.context.Name ?? `${accessory.displayName} Stateful Programmable Switch`, + Name: accessory.context.StatefulProgrammableSwitch.Name ?? `${accessory.displayName} Stateful Programmable Switch`, Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, On: accessory.context.On ?? false, }; + accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`); this.StatefulProgrammableSwitch.Service @@ -381,7 +363,6 @@ export class Others extends irdeviceBase { return this.StatefulProgrammableSwitch!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.StatefulProgrammableSwitch.Name; // Remove other services this.removeFanService(accessory); @@ -395,14 +376,13 @@ export class Others extends irdeviceBase { this.removeWindowCoveringService(accessory); } else { // Initialize Outlet Service - if (!accessory.context.Outlet) { - accessory.context.Outlet = {}; - } + accessory.context.Outlet = accessory.context.Outlet ?? {}; this.Outlet = { - Name: accessory.context.Name ?? `${accessory.displayName} Outlet`, + Name: accessory.context.Outlet.Name ?? `${accessory.displayName} Outlet`, Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, On: accessory.context.On ?? false, }; + accessory.context.Outlet = this.Outlet as object; this.debugWarnLog(`${this.device.remoteType}: ${accessory.displayName} Displaying as Outlet`); this.Outlet.Service @@ -412,7 +392,6 @@ export class Others extends irdeviceBase { return this.Outlet!.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Name = this.Outlet.Name; // Remove other services this.removeFanService(accessory); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index b3e230c3..7737bf9b 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -45,9 +45,7 @@ export class TV extends irdeviceBase { super(platform, accessory, device); // Initialize Television Service - if (!accessory.context.Television) { - accessory.context.Television = {}; - } + accessory.context.Television = accessory.context.Television ?? {}; this.Television = { Name: accessory.context.Television.Name ?? `${accessory.displayName} ${device.remoteType}`, ConfiguredName: accessory.context.Television.ConfiguredName ?? `${accessory.displayName} ${device.remoteType}`, @@ -57,6 +55,7 @@ export class TV extends irdeviceBase { SleepDiscoveryMode: accessory.context.SleepDiscoveryMode ?? this.hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, RemoteKey: accessory.context.RemoteKey ?? this.hap.Characteristic.RemoteKey.EXIT, }; + accessory.context.Television = this.Television as object; switch (device.remoteType) { case 'Speaker': @@ -103,12 +102,9 @@ export class TV extends irdeviceBase { return this.Television.RemoteKey; }) .onSet(this.RemoteKeySet.bind(this)); - accessory.context.Television.Name = this.Television.Name; // Initialize TelevisionSpeaker Service - if (!accessory.context.TelevisionSpeaker) { - accessory.context.TelevisionSpeaker = {}; - } + accessory.context.TelevisionSpeaker = accessory.context.TelevisionSpeaker ?? {}; this.TelevisionSpeaker = { Name: accessory.context.TelevisionSpeaker.Name ?? `${accessory.displayName} ${device.remoteType} Speaker`, Service: accessory.getService(this.hap.Service.TelevisionSpeaker) ?? accessory.addService(this.hap.Service.TelevisionSpeaker) as Service, @@ -116,6 +112,7 @@ export class TV extends irdeviceBase { VolumeControlType: accessory.context.VolumeControlType ?? this.hap.Characteristic.VolumeControlType.ABSOLUTE, VolumeSelector: accessory.context.VolumeSelector ?? this.hap.Characteristic.VolumeSelector.INCREMENT, }; + accessory.context.TelevisionSpeaker = this.TelevisionSpeaker as object; this.TelevisionSpeaker.Service .setCharacteristic(this.hap.Characteristic.Name, this.TelevisionSpeaker.Name) @@ -126,7 +123,6 @@ export class TV extends irdeviceBase { return this.TelevisionSpeaker.VolumeSelector; }) .onSet(this.VolumeSelectorSet.bind(this)); - accessory.context.TelevisionSpeaker.Name = this.TelevisionSpeaker.Name; } async VolumeSelectorSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index fcb30596..e102be48 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -31,14 +31,13 @@ export class VacuumCleaner extends irdeviceBase { super(platform, accessory, device); // Initialize Switch Service - if (!accessory.context.Switch) { - accessory.context.Switch = {}; - } + accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { - Name: accessory.context.Name ?? `${accessory.displayName} ${device.remoteType}`, + Name: accessory.context.Switch.Name ?? `${accessory.displayName} ${device.remoteType}`, Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, On: accessory.context.On ?? false, }; + accessory.context.Switch = this.Switch as object; this.Switch.Service .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name) @@ -47,7 +46,6 @@ export class VacuumCleaner extends irdeviceBase { return this.Switch.On; }) .onSet(this.OnSet.bind(this)); - accessory.context.Switch.Name = this.Switch.Name; } async OnSet(value: CharacteristicValue): Promise { diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index a7343846..cac76730 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -31,14 +31,13 @@ export class WaterHeater extends irdeviceBase { super(platform, accessory, device); // Initialize Switch Service - if (!accessory.context.Valve) { - accessory.context.Valve = {}; - } + accessory.context.Valve = accessory.context.Valve ?? {}; this.Valve = { - Name: accessory.context.Name ?? `${accessory.displayName} ${device.remoteType}`, + Name: accessory.context.Valve.Name ?? `${accessory.displayName} ${device.remoteType}`, Service: accessory.getService(this.hap.Service.Valve) ?? accessory.addService(this.hap.Service.Valve) as Service, Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE, }; + accessory.context.Valve = this.Valve as object; this.Valve.Service .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) @@ -48,7 +47,6 @@ export class WaterHeater extends irdeviceBase { return this.Valve.Active; }) .onSet(this.ActiveSet.bind(this)); - accessory.context.Valve.Name = this.Valve.Name; } async ActiveSet(value: CharacteristicValue): Promise { From 43faa3c5931ef82b099db9b97dab2d91be322fb1 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 17:05:40 -0500 Subject: [PATCH 67/73] resolve more firmware issues --- src/device/blindtilt.ts | 11 +++-- src/device/bot.ts | 9 ++-- src/device/ceilinglight.ts | 9 ++-- src/device/colorbulb.ts | 10 +++-- src/device/contact.ts | 9 ++-- src/device/curtain.ts | 15 ++++--- src/device/device.ts | 74 +++++++++++++++++++++----------- src/device/fan.ts | 9 ++-- src/device/hub.ts | 9 ++-- src/device/humidifier.ts | 9 ++-- src/device/iosensor.ts | 10 +++-- src/device/lightstrip.ts | 9 ++-- src/device/lock.ts | 9 ++-- src/device/meter.ts | 9 ++-- src/device/meterplus.ts | 10 +++-- src/device/motion.ts | 9 ++-- src/device/plug.ts | 9 ++-- src/device/robotvacuumcleaner.ts | 9 ++-- src/device/waterdetector.ts | 9 ++-- 19 files changed, 164 insertions(+), 83 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index a367096b..12c51f71 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -461,12 +461,17 @@ export class BlindTilt extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const replace = version?.replace(/^V|-.*$/g, ''); + const match = replace?.match(/.{1,1}/g); + const blindTiltVersion = match?.join('.') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, blindTiltVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, blindTiltVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(blindTiltVersion); + this.accessory.context.deviceVersion = blindTiltVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/bot.ts b/src/device/bot.ts index 106bb1d9..3bc754aa 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -592,12 +592,15 @@ export class Bot extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index f7aae35c..9aeada36 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -295,12 +295,15 @@ export class CeilingLight extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 34a4b2b3..ee9ae67b 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -63,7 +63,6 @@ export class ColorBulb extends deviceBase { ColorTemperature: accessory.context.ColorTemperature ?? 140, }; accessory.context.LightBulb = this.LightBulb as object; - this.errorLog(`${device.deviceType}: ${accessory.displayName} LightBulb Context: ${JSON.stringify(accessory.context.LightBulb)}`); // Adaptive Lighting if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) { @@ -350,12 +349,15 @@ export class ColorBulb extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/contact.ts b/src/device/contact.ts index 564e9609..38a90508 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -309,12 +309,15 @@ export class Contact extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 03d8e8cd..bc2021ca 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -422,10 +422,8 @@ export class Curtain extends deviceBase { } else { this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } async openAPIparseStatus(deviceStatus: deviceStatus): Promise { @@ -505,12 +503,15 @@ export class Curtain extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/device.ts b/src/device/device.ts index 94577466..adc4a043 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -68,10 +68,10 @@ export abstract class deviceBase { accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) - .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) - .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId); + .setCharacteristic(this.hap.Characteristic.Name, device.deviceName) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName) + .setCharacteristic(this.hap.Characteristic.Model, device.model!) + .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId); } async getDeviceLogSettings(device: device & devicesConfig): Promise { @@ -539,29 +539,55 @@ export abstract class deviceBase { device.bleModel = SwitchBotBLEModel.Unknown; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); } - accessory.context.name = device.deviceName; accessory.context.model = device.model; - accessory.context.deviceId = device.deviceId; - accessory.context.deviceType = device.deviceType; + let deviceFirmwareVersion: string; if (device.firmware) { - accessory.context.version = device.firmware; - } else if (device.firmware === undefined && device.version === undefined && accessory.context.version === undefined) { - device.version = this.platform.version; - accessory.context.version = this.platform.version; - } else if (accessory.context.device.version) { - accessory.context.version = accessory.context.device.version.replace(/^V|-.*$/g, ''); - }else { - accessory.context.version = device.version?.replace(/^V|-.*$/g, ''); - } - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${accessory.context.device.version}`); - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.HardwareRevision, accessory.context.version) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(accessory.context.version); - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Context: ${JSON.stringify(accessory.context.device)}`); + deviceFirmwareVersion = device.firmware; + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); + } else if (device.version) { + deviceFirmwareVersion = device.version; + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 FirmwareRevision: ${device.version}`); + } else if (accessory.context.deviceVersion) { + deviceFirmwareVersion = accessory.context.deviceVersion; + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`); + } else { + deviceFirmwareVersion = this.platform.version ?? '0.0.0'; + if (this.platform.version) { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`); + } else { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); + } + } + if (device.deviceType === 'Blind Tilt') { + // Firmware Version + const version = deviceFirmwareVersion.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + const replace = version?.replace(/^V|-.*$/g, ''); + const match = replace?.match(/.{1,1}/g); + const blindTiltVersion = match?.join('.') ?? '0.0.0'; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, blindTiltVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, blindTiltVersion) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(blindTiltVersion); + this.accessory.context.deviceVersion = blindTiltVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + } else { + // Firmware Version + const version = deviceFirmwareVersion.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + } } async statusCode(statusCode: number): Promise { diff --git a/src/device/fan.ts b/src/device/fan.ts index 9e6d9e25..429d7d8e 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -245,12 +245,15 @@ export class Fan extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/hub.ts b/src/device/hub.ts index 27d95694..d992a80a 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -216,12 +216,15 @@ export class Hub extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index a4a5ddec..3ab97148 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -271,12 +271,15 @@ export class Humidifier extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index b711bdee..645e8e9f 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -232,16 +232,20 @@ export class IOSensor extends deviceBase { this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } + // Firmware Version const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index af5e9193..16d2ff1d 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -334,12 +334,15 @@ export class StripLight extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/lock.ts b/src/device/lock.ts index 4346c24c..e6c5180a 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -274,12 +274,15 @@ export class Lock extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 45eff4ea..785608cc 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -230,12 +230,15 @@ export class Meter extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 73b26dc9..d869d508 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -227,16 +227,20 @@ export class MeterPlus extends deviceBase { this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`); } + // Firmware Version const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/motion.ts b/src/device/motion.ts index 46ff5561..23ef21ad 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -238,12 +238,15 @@ export class Motion extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/plug.ts b/src/device/plug.ts index 78680176..fc55ca15 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -135,12 +135,15 @@ export class Plug extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 3dd8aad1..954fce1f 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -229,12 +229,15 @@ export class RobotVacuumCleaner extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index e9c72904..589859e5 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -183,12 +183,15 @@ export class WaterDetector extends deviceBase { const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); if (deviceStatus.body.version) { - this.accessory.context.version = version?.replace(/^V|-.*$/g, ''); + const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; this.accessory .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } } From ca6b2870857e85269b770202a27d5e95db16caa9 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 17:44:33 -0500 Subject: [PATCH 68/73] device version without periods --- src/device/blindtilt.ts | 22 +++++++++++++--------- src/device/device.ts | 41 +++++++++++++++++------------------------ 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 12c51f71..4e726573 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -460,19 +460,23 @@ export class BlindTilt extends deviceBase { // Firmware Version const version = deviceStatus.body.version?.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); - if (deviceStatus.body.version) { + let deviceVersion: string; + if (version?.includes('.') === false) { const replace = version?.replace(/^V|-.*$/g, ''); const match = replace?.match(/.{1,1}/g); const blindTiltVersion = match?.join('.') ?? '0.0.0'; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.HardwareRevision, blindTiltVersion) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, blindTiltVersion) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(blindTiltVersion); - this.accessory.context.deviceVersion = blindTiltVersion; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + deviceVersion = blindTiltVersion; + } else { + deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; } + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } async refreshStatus(): Promise { diff --git a/src/device/device.ts b/src/device/device.ts index adc4a043..c0114218 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -559,35 +559,28 @@ export abstract class deviceBase { this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); } } - if (device.deviceType === 'Blind Tilt') { - // Firmware Version - const version = deviceFirmwareVersion.toString(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + + + // Firmware Version + const version = deviceFirmwareVersion.toString(); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + let deviceVersion: string; + if (version?.includes('.') === false) { const replace = version?.replace(/^V|-.*$/g, ''); const match = replace?.match(/.{1,1}/g); const blindTiltVersion = match?.join('.') ?? '0.0.0'; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.HardwareRevision, blindTiltVersion) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, blindTiltVersion) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(blindTiltVersion); - this.accessory.context.deviceVersion = blindTiltVersion; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + deviceVersion = blindTiltVersion; } else { - // Firmware Version - const version = deviceFirmwareVersion.toString(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); - const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(deviceVersion); - this.accessory.context.deviceVersion = deviceVersion; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; } + this.accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(deviceVersion); + this.accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); } async statusCode(statusCode: number): Promise { From c3c400e53ebb4b5fb466fd5ba164b6f02f555522 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 17:49:28 -0500 Subject: [PATCH 69/73] Update device.ts --- src/device/device.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index c0114218..a8b1fbda 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -544,23 +544,22 @@ export abstract class deviceBase { let deviceFirmwareVersion: string; if (device.firmware) { deviceFirmwareVersion = device.firmware; - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); } else if (device.version) { deviceFirmwareVersion = device.version; - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 FirmwareRevision: ${device.version}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 FirmwareRevision: ${device.version}`); } else if (accessory.context.deviceVersion) { deviceFirmwareVersion = accessory.context.deviceVersion; - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`); } else { deviceFirmwareVersion = this.platform.version ?? '0.0.0'; if (this.platform.version) { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`); } else { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); + this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); } } - // Firmware Version const version = deviceFirmwareVersion.toString(); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); From 4266a50734dcb8485e4b0dadc9c6273d15a32787 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 18:42:23 -0500 Subject: [PATCH 70/73] Update device.ts --- src/device/device.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/device.ts b/src/device/device.ts index a8b1fbda..fba44e79 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -567,8 +567,8 @@ export abstract class deviceBase { if (version?.includes('.') === false) { const replace = version?.replace(/^V|-.*$/g, ''); const match = replace?.match(/.{1,1}/g); - const blindTiltVersion = match?.join('.') ?? '0.0.0'; - deviceVersion = blindTiltVersion; + const validVersion = match?.join('.'); + deviceVersion = validVersion ?? '0.0.0'; } else { deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; } From 0c6ec58e279b8fb7eb3ba05489da2528a06eaf30 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 22:46:12 -0500 Subject: [PATCH 71/73] potentail fix for TypeError: Cannot set properties of undefined (setting 'Service') https://github.com/OpenWonderLabs/homebridge-switchbot/issues/971#issuecomment-2121504450 --- src/device/blindtilt.ts | 9 +- src/device/bot.ts | 566 ++++++++++++++++++++---------------- src/device/contact.ts | 16 +- src/device/curtain.ts | 9 +- src/device/device.ts | 13 +- src/device/hub.ts | 24 +- src/device/humidifier.ts | 8 +- src/device/iosensor.ts | 16 +- src/device/lock.ts | 18 +- src/device/meter.ts | 16 +- src/device/meterplus.ts | 16 +- src/device/motion.ts | 8 +- src/device/waterdetector.ts | 8 +- src/irdevice/other.ts | 100 ++++--- 14 files changed, 485 insertions(+), 342 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 4e726573..08cc2827 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -150,9 +150,12 @@ export class BlindTilt extends deviceBase { // Initialize LightSensor Service if (device.blindTilt?.hide_lightsensor) { - this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; - accessory.removeService(this.LightSensor!.Service); + if (this.LightSensor?.Service) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor.Service); + accessory.context.LightSensor = {}; + } } else { accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { diff --git a/src/device/bot.ts b/src/device/bot.ts index 3bc754aa..45b0fa26 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -744,9 +744,9 @@ export class Bot extends deviceBase { this.accessory.context.On = On; setTimeout(() => { if (this.botDeviceType === 'switch') { - this.Switch!.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); + this.Switch?.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); } else { - this.Outlet!.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); + this.Outlet?.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}, Switch Timeout`); }, 500); @@ -868,81 +868,99 @@ export class Bot extends deviceBase { */ async OnSet(value: CharacteristicValue): Promise { if (this.botDeviceType === 'garagedoor') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`); - if (value === this.hap.Characteristic.TargetDoorState.CLOSED) { - await this.setOn(false); - this.GarageDoor!.On = false; - } else { - await this.setOn(true); - this.GarageDoor!.On = true; + if (this.GarageDoor) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`); + if (value === this.hap.Characteristic.TargetDoorState.CLOSED) { + await this.setOn(false); + this.GarageDoor.On = false; + } else { + await this.setOn(true); + this.GarageDoor.On = true; + } } } else if (this.botDeviceType === 'door') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); - if (value === 0) { - await this.setOn(false); - this.Door!.On = false; - } else { - await this.setOn(true); - this.Door!.On = true; + if (this.Door) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Door.On = false; + } else { + await this.setOn(true); + this.Door.On = true; + } } } else if (this.botDeviceType === 'window') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); - if (value === 0) { - await this.setOn(false); - this.Window!.On = false; - } else { - await this.setOn(true); - this.Window!.On = true; + if (this.Window) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Window.On = false; + } else { + await this.setOn(true); + this.Window.On = true; + } } } else if (this.botDeviceType === 'windowcovering') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); - if (value === 0) { - await this.setOn(false); - this.WindowCovering!.On = false; - } else { - await this.setOn(true); - this.WindowCovering!.On = true; + if (this.WindowCovering) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`); + if (value === 0) { + await this.setOn(false); + this.WindowCovering.On = false; + } else { + await this.setOn(true); + this.WindowCovering.On = true; + } } } else if (this.botDeviceType === 'lock') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); - if (value === this.hap.Characteristic.LockTargetState.SECURED) { - await this.setOn(false); - this.LockMechanism!.On = false; - } else { - await this.setOn(true); - this.LockMechanism!.On = true; + if (this.LockMechanism) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`); + if (value === this.hap.Characteristic.LockTargetState.SECURED) { + await this.setOn(false); + this.LockMechanism.On = false; + } else { + await this.setOn(true); + this.LockMechanism.On = true; + } } } else if (this.botDeviceType === 'faucet') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); - if (value === this.hap.Characteristic.Active.INACTIVE) { - await this.setOn(false); - this.Faucet!.On = false; - } else { - await this.setOn(true); - this.Faucet!.On = true; + if (this.Faucet) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`); + if (value === this.hap.Characteristic.Active.INACTIVE) { + await this.setOn(false); + this.Faucet.On = false; + } else { + await this.setOn(true); + this.Faucet.On = true; + } } } else if (this.botDeviceType === 'stateful') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); - if (value === 0) { - await this.setOn(false); - this.StatefulProgrammableSwitch!.On = false; - } else { - await this.setOn(true); - this.StatefulProgrammableSwitch!.On = true; + if (this.StatefulProgrammableSwitch) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); + if (value === 0) { + await this.setOn(false); + this.StatefulProgrammableSwitch.On = false; + } else { + await this.setOn(true); + this.StatefulProgrammableSwitch.On = true; + } } } else if (this.botDeviceType === 'switch') { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); - if (value === 0) { - await this.setOn(false); - this.Switch!.On = false; - } else { - await this.setOn(true); - this.Switch!.On = true; + if (this.Switch) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`); + if (value === 0) { + await this.setOn(false); + this.Switch.On = false; + } else { + await this.setOn(true); + this.Switch.On = true; + } } } else { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); - await this.setOn(Boolean(value)); - this.Outlet!.On = value; + if (this.Outlet) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); + await this.setOn(Boolean(value)); + this.Outlet.On = value; + } } if (this.device.bot?.mode === 'multipress') { if (value === true) { @@ -959,177 +977,177 @@ export class Bot extends deviceBase { async updateHomeKitCharacteristics(): Promise { // State if (this.botDeviceType === 'garagedoor') { - if (this.GarageDoor!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.GarageDoor!.On}`); + if (this.GarageDoor?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.GarageDoor?.On}`); } else { - if (this.GarageDoor!.On) { - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN); + if (this.GarageDoor.On) { + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor.On})`); } else { - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor!.On})`); + + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor.On})`); } } - await this.setOn(Boolean(this.GarageDoor!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor!.On}`); + await this.setOn(Boolean(this.GarageDoor?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor?.On}`); } else if (this.botDeviceType === 'door') { - if (this.Door!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Door!.On}`); + if (this.Door?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Door?.On}`); } else { - if (this.Door!.On) { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.Door.On) { + this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door.On})`); } else { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door.On})`); } } - await this.setOn(Boolean(this.Door!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.Door!.On}`); + await this.setOn(Boolean(this.Door?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.Door?.On}`); } else if (this.botDeviceType === 'window') { - if (this.Window!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Window!.On}`); + if (this.Window?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Window?.On}`); } else { - if (this.Window!.On) { - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.Window.On) { + this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window.On})`); } else { - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window.On})`); } } - await this.setOn(Boolean(this.Window!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.Window!.On}`); + await this.setOn(Boolean(this.Window?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.Window?.On}`); } else if (this.botDeviceType === 'windowcovering') { - if (this.WindowCovering!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.WindowCovering!.On}`); + if (this.WindowCovering?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.WindowCovering?.On}`); } else { - if (this.WindowCovering!.On) { - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.WindowCovering.On) { + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering!.On})`); + + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering.On})`); } else { - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering!.On})`); + + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering.On})`); } } - await this.setOn(Boolean(this.WindowCovering!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering!.On}`); + await this.setOn(Boolean(this.WindowCovering?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering?.On}`); } else if (this.botDeviceType === 'lock') { - if (this.LockMechanism!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockMechanism!.On}`); + if (this.LockMechanism?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockMechanism?.On}`); } else { - if (this.LockMechanism!.On) { - this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + if (this.LockMechanism.On) { + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED); - this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc` - + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.LockMechanism!.On})`); + + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.LockMechanism.On})`); } else { - this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.LockMechanism!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.LockMechanism!.On})`); + + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.LockMechanism.On})`); } } - await this.setOn(Boolean(this.LockMechanism!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.LockMechanism!.On}`); + await this.setOn(Boolean(this.LockMechanism?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.LockMechanism?.On}`); } else if (this.botDeviceType === 'faucet') { - if (this.Faucet!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Faucet!.On}`); + if (this.Faucet?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Faucet?.On}`); } else { - if (this.Faucet!.On) { - this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); + if (this.Faucet.On) { + this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet.On}`); } else { - this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet!.On}`); + this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet.On}`); } } - await this.setOn(Boolean(this.Faucet!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.Faucet!.On}`); + await this.setOn(Boolean(this.Faucet?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.Faucet?.On}`); } else if (this.botDeviceType === 'fan') { - if (this.Fan!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan!.On}`); + if (this.Fan?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan?.On}`); } else { - if (this.Fan!.On) { - this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); + if (this.Fan.On) { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan.On}`); } else { - this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan!.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan!.On}`); + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan.On}`); } } - await this.setOn(Boolean(this.Fan!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.Fan!.On}`); + await this.setOn(Boolean(this.Fan?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.Fan?.On}`); } else if (this.botDeviceType === 'stateful') { - if (this.StatefulProgrammableSwitch!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.StatefulProgrammableSwitch!.On}`); + if (this.StatefulProgrammableSwitch?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.StatefulProgrammableSwitch?.On}`); } else { - if (this.StatefulProgrammableSwitch!.On) { - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + if (this.StatefulProgrammableSwitch.On) { + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch!.On})`); + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch.On})`); } else { - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` - + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch!.On})`); + + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch.On})`); } } - await this.setOn(Boolean(this.StatefulProgrammableSwitch!.On)); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch!.On}`); + await this.setOn(Boolean(this.StatefulProgrammableSwitch?.On)); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch?.On}`); } else if (this.botDeviceType === 'switch') { - if (this.Switch!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Switch!.On}`); + if (this.Switch?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Switch?.On}`); } else { - this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch!.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch!.On}`); - await this.setOn(Boolean(this.Switch!.On)); + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`); } + await this.setOn(Boolean(this.Switch?.On)); } else { - if (this.Outlet!.On === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet!.On}`); + if (this.Outlet?.On === undefined) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet?.On}`); } else { - this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet!.On); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet!.On}`); + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet.On); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet.On}`); } + await this.setOn(Boolean(this.Outlet?.On)); } - await this.setOn(Boolean(this.Outlet!.On)); this.accessory.context.On = await this.getOn(); // BatteryLevel if (this.Battery.BatteryLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`); } else { this.accessory.context.BatteryLevel = this.Battery.BatteryLevel; - this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`); } // StatusLowBattery @@ -1137,7 +1155,7 @@ export class Bot extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`); } else { this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery; - this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } @@ -1146,140 +1164,160 @@ export class Bot extends deviceBase { async removeOutletService(accessory: PlatformAccessory): Promise { // If outletService still present, then remove first if (this.Outlet?.Service) { - this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + this.Outlet.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`); - accessory.removeService(this.Outlet!.Service); + accessory.removeService(this.Outlet.Service); } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If garageDoorService still present, then remove first if (this.GarageDoor?.Service) { - this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + this.GarageDoor.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - accessory.removeService(this.GarageDoor!.Service); + accessory.removeService(this.GarageDoor.Service); } } async removeDoorService(accessory: PlatformAccessory): Promise { // If doorService still present, then remove first if (this.Door?.Service) { - this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + this.Door.Service = this.accessory.getService(this.hap.Service.Door) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`); - accessory.removeService(this.Door!.Service); + accessory.removeService(this.Door.Service); } } async removeLockService(accessory: PlatformAccessory): Promise { // If lockService still present, then remove first if (this.LockMechanism?.Service) { - this.LockMechanism!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + this.LockMechanism.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.LockMechanism!.Service); + accessory.removeService(this.LockMechanism.Service); } } async removeFaucetService(accessory: PlatformAccessory): Promise { // If faucetService still present, then remove first if (this.Faucet?.Service) { - this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; + this.Faucet.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Faucet Service`); - accessory.removeService(this.Faucet!.Service); + accessory.removeService(this.Faucet.Service); } } async removeFanService(accessory: PlatformAccessory): Promise { // If fanService still present, then remove first if (this.Fan?.Service) { - this.Fan!.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; + this.Fan.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`); - accessory.removeService(this.Fan!.Service); + accessory.removeService(this.Fan.Service); } } async removeWindowService(accessory: PlatformAccessory): Promise { // If windowService still present, then remove first if (this.Window?.Service) { - this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + this.Window.Service = this.accessory.getService(this.hap.Service.Window) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`); - accessory.removeService(this.Window!.Service); + accessory.removeService(this.Window.Service); } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If windowCoveringService still present, then remove first if (this.WindowCovering?.Service) { - this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + this.WindowCovering.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - accessory.removeService(this.WindowCovering!.Service); + accessory.removeService(this.WindowCovering.Service); } } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If statefulProgrammableSwitchService still present, then remove first if (this.StatefulProgrammableSwitch?.Service) { - this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; + this.StatefulProgrammableSwitch.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); - accessory.removeService(this.StatefulProgrammableSwitch!.Service); + accessory.removeService(this.StatefulProgrammableSwitch.Service); } } async removeSwitchService(accessory: PlatformAccessory): Promise { // If switchService still present, then remove first if (this.Switch?.Service) { - this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + this.Switch.Service = this.accessory.getService(this.hap.Service.Switch) as Service; this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`); - accessory.removeService(this.Switch!.Service); + accessory.removeService(this.Switch.Service); } } async getOn(): Promise { let On: boolean; if (this.botDeviceType === 'garagedoor') { - On = this.GarageDoor!.On ? true : false; + On = this.GarageDoor?.On ? true : false; } else if (this.botDeviceType === 'door') { - On = this.Door!.On ? true : false; + On = this.Door?.On ? true : false; } else if (this.botDeviceType === 'window') { - On = this.Window!.On ? true : false; + On = this.Window?.On ? true : false; } else if (this.botDeviceType === 'windowcovering') { - On = this.WindowCovering!.On ? true : false; + On = this.WindowCovering?.On ? true : false; } else if (this.botDeviceType === 'lock') { - On = this.LockMechanism!.On ? true : false; + On = this.LockMechanism?.On ? true : false; } else if (this.botDeviceType === 'faucet') { - On = this.Faucet!.On ? true : false; + On = this.Faucet?.On ? true : false; } else if (this.botDeviceType === 'fan') { - On = this.Fan!.On ? true : false; + On = this.Fan?.On ? true : false; } else if (this.botDeviceType === 'stateful') { - On = this.StatefulProgrammableSwitch!.On ? true : false; + On = this.StatefulProgrammableSwitch?.On ? true : false; } else if (this.botDeviceType === 'switch') { - On = this.Switch!.On ? true : false; + On = this.Switch?.On ? true : false; } else { - On = this.Outlet!.On ? true : false; + On = this.Outlet?.On ? true : false; } return On; } async setOn(On: boolean): Promise { if (this.botDeviceType === 'garagedoor') { - this.GarageDoor!.On = On; + if (this.GarageDoor) { + this.GarageDoor.On = On; + } } else if (this.botDeviceType === 'door') { - this.Door!.On = On; + if (this.Door) { + this.Door.On = On; + } } else if (this.botDeviceType === 'window') { - this.Window!.On = On; + if (this.Window) { + this.Window.On = On; + } } else if (this.botDeviceType === 'windowcovering') { - this.WindowCovering!.On = On; + if (this.WindowCovering) { + this.WindowCovering.On = On; + } } else if (this.botDeviceType === 'lock') { - this.LockMechanism!.On = On; + if (this.LockMechanism) { + this.LockMechanism.On = On; + } } else if (this.botDeviceType === 'faucet') { - this.Faucet!.On = On; + if (this.Faucet) { + this.Faucet.On = On; + } } else if (this.botDeviceType === 'fan') { - this.Fan!.On = On; + if (this.Fan) { + this.Fan.On = On; + } } else if (this.botDeviceType === 'stateful') { - this.StatefulProgrammableSwitch!.On = On; + if (this.StatefulProgrammableSwitch) { + this.StatefulProgrammableSwitch.On = On; + } } else if (this.botDeviceType === 'switch') { - this.Switch!.On = On; + if (this.Switch) { + this.Switch.On = On; + } } else { - this.Outlet!.On = On; + if (this.Outlet) { + this.Outlet.On = On; + } } } @@ -1363,71 +1401,111 @@ export class Bot extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { if (this.botDeviceType === 'garagedoor') { - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, false); + if (this.GarageDoor) { + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, false); + } } else if (this.botDeviceType === 'door') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.Door) { + this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } } else if (this.botDeviceType === 'window') { - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.Window) { + this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } } else if (this.botDeviceType === 'windowcovering') { - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + if (this.WindowCovering) { + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED); + } } else if (this.botDeviceType === 'lock') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + if (this.LockMechanism) { + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED); + } } else if (this.botDeviceType === 'faucet') { - this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + if (this.Faucet) { + this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE); + } } else if (this.botDeviceType === 'fan') { - this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, false); + if (this.Fan) { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, false); + } } else if (this.botDeviceType === 'stateful') { - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, - this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + if (this.StatefulProgrammableSwitch) { + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, + this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0); + } } else if (this.botDeviceType === 'switch') { - this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, false); + if (this.Switch) { + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, false); + } } else { - this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, false); + if (this.Outlet) { + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, false); + } } } } async apiError(e: any): Promise { if (this.botDeviceType === 'garagedoor') { - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + if (this.GarageDoor) { + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + } } else if (this.botDeviceType === 'door') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.Door) { + this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.botDeviceType === 'window') { - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.Window) { + this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.botDeviceType === 'windowcovering') { - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.WindowCovering) { + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.botDeviceType === 'lock') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + if (this.LockMechanism) { + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + } } else if (this.botDeviceType === 'faucet') { - this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + if (this.Faucet) { + this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + } } else if (this.botDeviceType === 'fan') { - this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Fan) { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } else if (this.botDeviceType === 'stateful') { - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + if (this.StatefulProgrammableSwitch) { + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + } } else if (this.botDeviceType === 'switch') { - this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Switch) { + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } else { - this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Outlet) { + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); diff --git a/src/device/contact.ts b/src/device/contact.ts index 38a90508..0708f484 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -103,9 +103,11 @@ export class Contact extends deviceBase { // Initialize Motion Sensor Service if (this.device.contact?.hide_motionsensor) { - this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); - this.MotionSensor!.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; - accessory.removeService(this.MotionSensor!.Service); + if (this.MotionSensor) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`); + this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) as Service; + accessory.removeService(this.MotionSensor.Service); + } } else { accessory.context.MotionSensor = accessory.context.MotionSensor ?? {}; this.MotionSensor = { @@ -127,9 +129,11 @@ export class Contact extends deviceBase { // Initialize Light Sensor Service if (device.contact?.hide_lightsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.LightSensor!.Service = accessory.getService(this.hap.Service.LightSensor) as Service; - accessory.removeService(this.LightSensor!.Service); + if (this.LightSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor.Service); + } } else { accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { diff --git a/src/device/curtain.ts b/src/device/curtain.ts index bc2021ca..a98011ec 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -148,9 +148,12 @@ export class Curtain extends deviceBase { // Initialize LightSensor Service if (device.curtain?.hide_lightsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; - accessory.removeService(this.LightSensor!.Service); + if (this.LightSensor?.Service) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor.Service); + accessory.context.LightSensor = {}; + } } else { accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { diff --git a/src/device/device.ts b/src/device/device.ts index fba44e79..8e40fc82 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -584,11 +584,18 @@ export abstract class deviceBase { async statusCode(statusCode: number): Promise { if (statusCode === 171) { - if (this.device.deviceId === this.device.hubDeviceId) { + const previousStatusCode = statusCode; + if (this.device.hubDeviceId === this.device.deviceId) { statusCode = 161; + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: ` + + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` + + `${this.device.deviceId}, meaning the device is it's own hub.`); } - if (this.device.deviceId === '000000000000') { + if (this.device.hubDeviceId === '000000000000') { statusCode = 161; + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: ` + + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` + + `${this.device.deviceId}, meaning the device is it's own hub.`); } } switch (statusCode) { @@ -656,7 +663,7 @@ export abstract class deviceBase { break; default: this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` - + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + + `${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`); } } diff --git a/src/device/hub.ts b/src/device/hub.ts index d992a80a..1808a597 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -48,9 +48,11 @@ export class Hub extends deviceBase { // Initialize Temperature Sensor Service if (device.hub?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; - accessory.removeService(this.TemperatureSensor!.Service); + if (this.TemperatureSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor.Service); + } } else { accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { @@ -78,9 +80,11 @@ export class Hub extends deviceBase { // Initialize Humidity Sensor Service if (device.hub?.hide_humidity) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; - accessory.removeService(this.HumiditySensor!.Service); + if (this.HumiditySensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); + this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor.Service); + } } else { accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { @@ -104,9 +108,11 @@ export class Hub extends deviceBase { // Initialize Light Sensor Service if (device.hub?.hide_lightsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; - accessory.removeService(this.LightSensor!.Service); + if (this.LightSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor.Service); + } } else { accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 3ab97148..26b55ffe 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -103,9 +103,11 @@ export class Humidifier extends deviceBase { // Initialize the Temperature Sensor Service if (device.humidifier?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; - accessory.removeService(this.TemperatureSensor!.Service); + if (this.TemperatureSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor!.Service); + } } else { accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 645e8e9f..87a79405 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -80,9 +80,11 @@ export class IOSensor extends deviceBase { // InitializeTemperature Sensor Service if (device.iosensor?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; - accessory.removeService(this.TemperatureSensor!.Service); + if (this.TemperatureSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor.Service); + } } else { accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { @@ -110,9 +112,11 @@ export class IOSensor extends deviceBase { // Initialize Humidity Sensor Service if (device.iosensor?.hide_humidity) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; - accessory.removeService(this.HumiditySensor!.Service); + if (this.HumiditySensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); + this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor.Service); + } } else { accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { diff --git a/src/device/lock.ts b/src/device/lock.ts index e6c5180a..f3d92889 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -96,9 +96,11 @@ export class Lock extends deviceBase { // Contact Sensor Service if (device.lock?.hide_contactsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); - this.ContactSensor!.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; - accessory.removeService(this.ContactSensor!.Service); + if (this.ContactSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`); + this.ContactSensor.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service; + accessory.removeService(this.ContactSensor.Service); + } } else { accessory.context.ContactSensor = accessory.context.ContactSensor ?? {}; this.ContactSensor = { @@ -119,10 +121,12 @@ export class Lock extends deviceBase { } // Initialize Latch Button Service - if (device.lock?.activate_latchbutton === false) { // remove the service when this variable is false - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Latch Button Service`); - this.Switch!.Service = accessory.getService(this.hap.Service.Switch) as Service; - accessory.removeService(this.Switch!.Service); + if (device.lock?.activate_latchbutton === false) { + if (this.Switch) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Latch Button Service`); + this.Switch.Service = accessory.getService(this.hap.Service.Switch) as Service; + accessory.removeService(this.Switch.Service); + } } else { accessory.context.Switch = accessory.context.Switch ?? {}; this.Switch = { diff --git a/src/device/meter.ts b/src/device/meter.ts index 785608cc..8e8718ea 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -74,9 +74,11 @@ export class Meter extends deviceBase { // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; - accessory.removeService(this.TemperatureSensor!.Service); + if (this.TemperatureSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor.Service); + } } else { accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { @@ -103,9 +105,11 @@ export class Meter extends deviceBase { } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; - accessory.removeService(this.HumiditySensor!.Service); + if (this.HumiditySensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); + this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor.Service); + } } else { accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index d869d508..fc0dc859 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -78,9 +78,11 @@ export class MeterPlus extends deviceBase { // Initialize Temperature Sensor Service if (device.meter?.hide_temperature) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); - this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; - accessory.removeService(this.TemperatureSensor!.Service); + if (this.TemperatureSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`); + this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service; + accessory.removeService(this.TemperatureSensor.Service); + } } else { accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}; this.TemperatureSensor = { @@ -107,9 +109,11 @@ export class MeterPlus extends deviceBase { } // Initialize Humidity Sensor Service if (device.meter?.hide_humidity) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); - this.HumiditySensor!.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; - accessory.removeService(this.HumiditySensor!.Service); + if (this.HumiditySensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`); + this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service; + accessory.removeService(this.HumiditySensor.Service); + } } else { accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { diff --git a/src/device/motion.ts b/src/device/motion.ts index 23ef21ad..37788aa5 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -98,9 +98,11 @@ export class Motion extends deviceBase { // Initialize Light Sensor Service if (device.motion?.hide_lightsensor) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); - this.LightSensor!.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; - accessory.removeService(this.LightSensor!.Service); + if (this.LightSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`); + this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service; + accessory.removeService(this.LightSensor.Service); + } } else { accessory.context.LightSensor = accessory.context.LightSensor ?? {}; this.LightSensor = { diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index 589859e5..ce8188e3 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -78,9 +78,11 @@ export class WaterDetector extends deviceBase { // Initialize Leak Sensor Service if (device.waterdetector?.hide_leak) { - this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); - this.LeakSensor!.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; - accessory.removeService(this.LeakSensor!.Service); + if (this.LeakSensor) { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); + this.LeakSensor.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; + accessory.removeService(this.LeakSensor.Service); + } } else { accessory.context.LeakSensor = accessory.context.LeakSensor ?? {}; this.LeakSensor = { diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index dfd00544..c12d5e9b 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -725,125 +725,145 @@ export class Others extends irdeviceBase { async apiError(e: any): Promise { if (this.otherDeviceType === 'garagedoor') { - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); - this.GarageDoor!.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + if (this.GarageDoor) { + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e); + this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e); + } } else if (this.otherDeviceType === 'door') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.Door) { + this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.otherDeviceType === 'window') { - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.Window!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.Window) { + this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.otherDeviceType === 'windowcovering') { - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); - this.WindowCovering!.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + if (this.WindowCovering) { + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e); + this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e); + } } else if (this.otherDeviceType === 'lock') { - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); - this.Door!.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + if (this.LockMechanism) { + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e); + this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e); + } } else if (this.otherDeviceType === 'faucet') { - this.Faucet!.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + if (this.Faucet) { + this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, e); + } } else if (this.otherDeviceType === 'fan') { - this.Fan!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Fan) { + this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } else if (this.otherDeviceType === 'stateful') { - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); - this.StatefulProgrammableSwitch!.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + if (this.StatefulProgrammableSwitch) { + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e); + this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e); + } } else if (this.otherDeviceType === 'switch') { - this.Switch!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Switch) { + this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } else { - this.Outlet!.Service.updateCharacteristic(this.hap.Characteristic.On, e); + if (this.Outlet) { + this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, e); + } } } async removeOutletService(accessory: PlatformAccessory): Promise { // If Outlet.Service still present, then remove first if (this.Outlet?.Service) { - this.Outlet!.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; + this.Outlet.Service = this.accessory.getService(this.hap.Service.Outlet) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Outlet Service`); - accessory.removeService(this.Outlet!.Service); + accessory.removeService(this.Outlet.Service); } } async removeGarageDoorService(accessory: PlatformAccessory): Promise { // If GarageDoor.Service still present, then remove first if (this.GarageDoor?.Service) { - this.GarageDoor!.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; + this.GarageDoor.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Garage Door Service`); - accessory.removeService(this.GarageDoor!.Service); + accessory.removeService(this.GarageDoor.Service); } } async removeDoorService(accessory: PlatformAccessory): Promise { // If Door.Service still present, then remove first if (this.Door?.Service) { - this.Door!.Service = this.accessory.getService(this.hap.Service.Door) as Service; + this.Door.Service = this.accessory.getService(this.hap.Service.Door) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Door Service`); - accessory.removeService(this.Door!.Service); + accessory.removeService(this.Door.Service); } } async removeLockService(accessory: PlatformAccessory): Promise { // If Lock.Service still present, then remove first if (this.LockMechanism?.Service) { - this.LockMechanism!.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; + this.LockMechanism.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Lock Service`); - accessory.removeService(this.LockMechanism!.Service); + accessory.removeService(this.LockMechanism.Service); } } async removeFaucetService(accessory: PlatformAccessory): Promise { // If Faucet.Service still present, then remove first if (this.Faucet?.Service) { - this.Faucet!.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; + this.Faucet.Service = this.accessory.getService(this.hap.Service.Faucet) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Faucet Service`); - accessory.removeService(this.Faucet!.Service); + accessory.removeService(this.Faucet.Service); } } async removeFanService(accessory: PlatformAccessory): Promise { // If Fan Service still present, then remove first if (this.Fan?.Service) { - this.Fan!.Service = this.accessory.getService(this.hap.Service.Fan) as Service; + this.Fan.Service = this.accessory.getService(this.hap.Service.Fan) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Fan Service`); - accessory.removeService(this.Fan!.Service); + accessory.removeService(this.Fan.Service); } } async removeWindowService(accessory: PlatformAccessory): Promise { // If Window.Service still present, then remove first if (this.Window?.Service) { - this.Window!.Service = this.accessory.getService(this.hap.Service.Window) as Service; + this.Window.Service = this.accessory.getService(this.hap.Service.Window) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Service`); - accessory.removeService(this.Window!.Service); + accessory.removeService(this.Window.Service); } } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { // If WindowCovering.Service still present, then remove first if (this.WindowCovering?.Service) { - this.WindowCovering!.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; + this.WindowCovering.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Window Covering Service`); - accessory.removeService(this.WindowCovering!.Service); + accessory.removeService(this.WindowCovering.Service); } } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { // If StatefulProgrammableSwitch.Service still present, then remove first if (this.StatefulProgrammableSwitch?.Service) { - this.StatefulProgrammableSwitch!.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; + this.StatefulProgrammableSwitch.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`); - accessory.removeService(this.StatefulProgrammableSwitch!.Service); + accessory.removeService(this.StatefulProgrammableSwitch.Service); } } async removeSwitchService(accessory: PlatformAccessory): Promise { // If Switch.Service still present, then remove first if (this.Switch?.Service) { - this.Switch!.Service = this.accessory.getService(this.hap.Service.Switch) as Service; + this.Switch.Service = this.accessory.getService(this.hap.Service.Switch) as Service; this.warnLog(`${this.device.remoteType}: ${accessory.displayName} Removing Leftover Switch Service`); - accessory.removeService(this.Switch!.Service); + accessory.removeService(this.Switch.Service); } } From 4be63612fde7310d688d430b72b67284c2e5e4fa Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Mon, 20 May 2024 23:15:58 -0500 Subject: [PATCH 72/73] refine --- src/device/blindtilt.ts | 4 +++- src/device/bot.ts | 2 +- src/device/ceilinglight.ts | 6 +++--- src/device/colorbulb.ts | 8 ++++---- src/device/curtain.ts | 2 +- src/device/fan.ts | 2 +- src/device/humidifier.ts | 6 +++--- src/device/lightstrip.ts | 6 +++--- src/device/lock.ts | 2 +- src/device/plug.ts | 2 +- src/device/robotvacuumcleaner.ts | 4 ++-- src/irdevice/airconditioner.ts | 2 +- src/irdevice/airpurifier.ts | 2 +- src/irdevice/camera.ts | 2 +- src/irdevice/fan.ts | 2 +- src/irdevice/light.ts | 2 +- src/irdevice/other.ts | 2 +- src/irdevice/tv.ts | 2 +- src/irdevice/vacuumcleaner.ts | 2 +- src/irdevice/waterheater.ts | 2 +- 20 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 08cc2827..38a58fc8 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -650,11 +650,13 @@ export class BlindTilt extends deviceBase { if (position === 100) { bodyChange = JSON.stringify({ command: 'fullyOpen', + parameter: 'default', commandType: 'command', }); } else if (position === 0) { bodyChange = JSON.stringify({ command: direction === 'up' ? 'closeUp' : 'closeDown', + parameter: 'default', commandType: 'command', }); } else { @@ -680,7 +682,7 @@ export class BlindTilt extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/bot.ts b/src/device/bot.ts index 45b0fa26..a0eebe4f 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -839,7 +839,7 @@ export class Bot extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 9aeada36..4575b6e9 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -505,7 +505,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -566,7 +566,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -654,7 +654,7 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index ee9ae67b..e5fe1917 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -695,7 +695,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -756,7 +756,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -802,7 +802,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -842,7 +842,7 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/curtain.ts b/src/device/curtain.ts index a98011ec..89562c88 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -710,7 +710,7 @@ export class Curtain extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/fan.ts b/src/device/fan.ts index 429d7d8e..35dac785 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -454,7 +454,7 @@ export class Fan extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 26b55ffe..11f54364 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -451,7 +451,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -506,7 +506,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -552,7 +552,7 @@ export class Humidifier extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 16d2ff1d..4e33b065 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -639,7 +639,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -697,7 +697,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -741,7 +741,7 @@ export class StripLight extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/lock.ts b/src/device/lock.ts index f3d92889..01b7d8e0 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -475,7 +475,7 @@ export class Lock extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/plug.ts b/src/device/plug.ts index fc55ca15..fa0adf09 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -342,7 +342,7 @@ export class Plug extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 954fce1f..3d0e043e 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -425,7 +425,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); @@ -464,7 +464,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(deviceStatus)} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index 9013efc1..eb95f160 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -374,7 +374,7 @@ export class AirConditioner extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 181d55a8..4347d3b4 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -247,7 +247,7 @@ export class AirPurifier extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index b30fbc0b..3448e599 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -118,7 +118,7 @@ export class Camera extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index 5153e43a..bd99887a 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -253,7 +253,7 @@ export class IRFan extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index 2b5fc2b5..fe68da5a 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -230,7 +230,7 @@ export class Light extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.accessory.context.On = On; this.updateHomeKitCharacteristics(); } else { diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index c12d5e9b..6bb3064d 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -548,7 +548,7 @@ export class Others extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 7737bf9b..57d543a7 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -380,7 +380,7 @@ export class TV extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index e102be48..ffad80ad 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -114,7 +114,7 @@ export class VacuumCleaner extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index cac76730..56fea9d3 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -116,7 +116,7 @@ export class WaterHeater extends irdeviceBase { this.debugSuccessLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.remoteType}: ${this.accessory.displayName}` - + ` request to SwitchBot API, body: ${JSON.stringify(bodyChange)} sent successfully`); + + ` request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); this.updateHomeKitCharacteristics(); } else { this.statusCode(statusCode); From 2f7d615829a77485d7dca470ce13a16f53221588 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Sun, 26 May 2024 17:09:29 -0600 Subject: [PATCH 73/73] v3.5.0 ## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-05-26) ### What's Changed - Add Support for `Water Detector` - Add Support for `Battery Circulator Fan` - Add BLE support for `Smart Lock` - Add `K10+` deviceType Support - Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev) - Major Refactoring of `device` and `irdevice` files. - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0 --- CHANGELOG.md | 2 +- config.schema.json | 67 +++-- package-lock.json | 427 +++++++++++++++++-------------- package.json | 8 +- src/device/blindtilt.ts | 209 ++++++--------- src/device/bot.ts | 84 +++--- src/device/ceilinglight.ts | 215 +++++++--------- src/device/colorbulb.ts | 287 +++++++++------------ src/device/contact.ts | 121 ++++----- src/device/curtain.ts | 179 +++++-------- src/device/device.ts | 225 ++++++++-------- src/device/fan.ts | 313 ++++++++++++++++------ src/device/hub.ts | 128 ++++----- src/device/humidifier.ts | 132 ++++------ src/device/iosensor.ts | 74 +++--- src/device/lightstrip.ts | 267 +++++++++---------- src/device/lock.ts | 102 ++++---- src/device/meter.ts | 79 +++--- src/device/meterplus.ts | 73 +++--- src/device/motion.ts | 95 +++---- src/device/plug.ts | 73 +++--- src/device/robotvacuumcleaner.ts | 105 ++++---- src/device/waterdetector.ts | 65 ++--- src/irdevice/airconditioner.ts | 96 +++---- src/irdevice/airpurifier.ts | 48 ++-- src/irdevice/camera.ts | 17 +- src/irdevice/fan.ts | 66 +---- src/irdevice/irdevice.ts | 58 ++++- src/irdevice/tv.ts | 12 +- src/platform.ts | 90 +++---- src/settings.ts | 2 + src/utils.ts | 8 +- 32 files changed, 1768 insertions(+), 1959 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a415d568..3fde7aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) -## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-05-XX) +## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-05-26) ### What's Changed - Add Support for `Water Detector` diff --git a/config.schema.json b/config.schema.json index ea1c47a6..08acf3b4 100644 --- a/config.schema.json +++ b/config.schema.json @@ -998,14 +998,29 @@ "type": "string", "placeholder": "1.2.8", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].configDeviceType);" } }, "refreshRate": { "title": "Device Refresh Rate", "type": "number", "placeholder": 360, - "description": "Indicates the number of seconds between polls of SwitchBot API.", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "updateRate": { + "title": "Device Update Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, "condition": { "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" } @@ -1014,7 +1029,7 @@ "title": "Offline as Off", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" } }, "external": { @@ -1648,16 +1663,19 @@ "placeholder": 3 }, "refreshRate": { - "title": "Refresh Rate", + "title": "Device Refresh Rate", "type": "number", - "placeholder": 360, - "description": "Indicates the number of seconds between polls of SwitchBot API." + "placeholder": 360 + }, + "updateRate": { + "title": "Device Update Rate", + "type": "number", + "placeholder": 5 }, "pushRate": { - "title": "Push Rate", + "title": "Device Push Rate", "type": "number", - "placeholder": 1, - "description": "Indicates the number of seconds between pushes to SwitchBot API." + "placeholder": 1 }, "logging": { "title": "Logging Setting", @@ -1789,7 +1807,18 @@ "options.devices[].mqttPubOptions", "options.devices[].history", "options.devices[].firmware", - "options.devices[].refreshRate", + { + "key": "options.devices[].refreshRate", + "description": "Specifies the interval, in seconds, for retrieving the latest device status from the SwitchBot API. This interval applies only to this specific device." + }, + { + "key": "options.devices[].updateRate", + "description": "Specifies the interval, in seconds, at which this device will request updates from the SwitchBot API while the device is in motion, for Curtain(s) and Blind Tilt(s) only." + }, + { + "key": "options.devices[].pushRate", + "description": "Specifies the interval, in seconds, between pushes to the SwitchBot API for this specific device." + }, "options.devices[].offline", "options.devices[].external", "options.devices[].logging" @@ -1855,22 +1884,18 @@ "options.webhookURL", "options.maxRetries", "options.delayBetweenRetries", - { - "type": "help", - "helpvalue": "
Refresh Rate
Refresh Rate indicates the number of seconds between polls of SwitchBot API." - }, { "key": "options.refreshRate", - "notitle": true - }, + "description": "Specifies the interval, in seconds, for retrieving the latest device status from the SwitchBot API." + }, { - "type": "help", - "helpvalue": "
Push Rate
Push Rate indicates the number of seconds between pushes to SwitchBot API, Currently only for Curtains." - }, + "key": "options.updateRate", + "description": "Specifies the interval, in seconds, at which devices will request updates from the SwitchBot API while in motion, for Curtain(s) and Blind Tilt(s) only." + }, { "key": "options.pushRate", - "notitle": true - }, + "description": "Specifies the interval, in seconds, between pushes to the SwitchBot API." + }, "options.logging" ] } diff --git a/package-lock.json b/package-lock.json index b967dd3b..272fbd64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", - "undici": "^6.17.0" + "undici": "^6.18.1" }, "devDependencies": { "@eslint/js": "^9.3.0", @@ -32,10 +32,10 @@ "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", "eslint": "^9.3.0", - "globals": "^15.2.0", + "globals": "^15.3.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", - "nodemon": "^3.1.0", + "nodemon": "^3.1.1", "npm-check-updates": "^16.14.20", "rimraf": "^5.0.7", "ts-node": "^10.9.2", @@ -47,7 +47,7 @@ "node": "^18 || ^20 || ^22" }, "optionalDependencies": { - "node-switchbot": "2.1.1-beta.0" + "node-switchbot": "2.1.1" } }, "node_modules/@abandonware/bluetooth-hci-socket": { @@ -223,9 +223,9 @@ } }, "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -632,6 +632,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "optional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -665,6 +666,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "optional": true, "dependencies": { "glob": "^7.1.3" @@ -1060,6 +1062,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1092,6 +1095,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -1184,6 +1188,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "delegates": "^1.0.0", @@ -1235,6 +1240,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1284,6 +1290,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -1303,6 +1310,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1494,6 +1502,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "are-we-there-yet": "^3.0.0", @@ -1509,6 +1518,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -1986,18 +1996,6 @@ "eslint": ">=8.40.0" } }, - "node_modules/@stylistic/eslint-plugin-jsx/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@stylistic/eslint-plugin-plus": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz", @@ -2012,13 +2010,13 @@ } }, "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2029,9 +2027,9 @@ } }, "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2042,13 +2040,13 @@ } }, "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2070,15 +2068,15 @@ } }, "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2092,12 +2090,12 @@ } }, "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "7.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2162,13 +2160,13 @@ } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2179,9 +2177,9 @@ } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2192,13 +2190,13 @@ } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2220,15 +2218,15 @@ } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2242,12 +2240,12 @@ } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "7.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2474,16 +2472,16 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.14.tgz", - "integrity": "sha512-tfw3zfCg+ynwARhVsuMXKBrmWCtqQ2Cr/cjPAuyKhJGY8t069Lc0Y+F5H7oDLlmm+G54v8lAHkTkw4K/p+PpFQ==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.16.tgz", + "integrity": "sha512-ZDVgR/z28jg3CPzQJqFIOQ/gshqf3NDw7zCu2jTeAYqtyXpCsAkAivvkeuuuXCypRl53cK16qDPlCguUCZW5Ow==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.14", - "@typescript-eslint/type-utils": "8.0.0-alpha.14", - "@typescript-eslint/utils": "8.0.0-alpha.14", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", + "@typescript-eslint/scope-manager": "8.0.0-alpha.16", + "@typescript-eslint/type-utils": "8.0.0-alpha.16", + "@typescript-eslint/utils": "8.0.0-alpha.16", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2507,15 +2505,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.14.tgz", - "integrity": "sha512-fD+DFo6aJJYyX4w712HzmE7QmUkoUvtlsFO/MqmYMeHIe0Pz5JZpJ1aYVbdxctazOb7NoW3p3RQgmpDcLT2pdQ==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.16.tgz", + "integrity": "sha512-L8eX2ggDQqb986+P9FZVsl/4M0vPplvgVzPkFFtPtsP2rVRSFpzGidZGzNN73RBq2G5KnWo87sx2mUrJ+99OZQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.0.0-alpha.14", - "@typescript-eslint/types": "8.0.0-alpha.14", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.14", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", + "@typescript-eslint/scope-manager": "8.0.0-alpha.16", + "@typescript-eslint/types": "8.0.0-alpha.16", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.16", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", "debug": "^4.3.4" }, "engines": { @@ -2535,13 +2533,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.14.tgz", - "integrity": "sha512-6EmhoNZzfjd/sZGxichVguWUGCCgT12xyw3ppNZ9bM/m6qQCE66BqudGxzD58UPL4PpN++Y8KqVItax0gNq4BQ==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.16.tgz", + "integrity": "sha512-SsN6Kf+sBK62CgDkW4XHZYDqCDwOY2d1Q4aUAOTcohhw06HiXYbY5xQ23GqOV2BL9TaKL+HuyyP+LLZ1aIG8FQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.14", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.14" + "@typescript-eslint/types": "8.0.0-alpha.16", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.16" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2552,13 +2550,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.14.tgz", - "integrity": "sha512-F/rtAXWMrFPO49xK0XLw7hYtPVrjj+jRJhJRRcSBWRybcu7rvlEQ/Chk+QXvyp15QuwmMD5jAqNI+Fkbxgc0gQ==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.16.tgz", + "integrity": "sha512-g5GJ0sB6WLu71fkPlMe9JV1o3p6AKAN0vUfg4XGyYPLSElRYdMMy4Nuq1Snq2Gqs1rceomHrogp5v/qH7Iq7ig==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.0-alpha.14", - "@typescript-eslint/utils": "8.0.0-alpha.14", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.16", + "@typescript-eslint/utils": "8.0.0-alpha.16", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2576,9 +2574,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.14.tgz", - "integrity": "sha512-2u0FBQ0usELnbTqZhHN6X8ngJlpCchFTroWFG5nvo0TOoiPYV+5AbGiRb0IWMsLfxSzeDJeasUzByVvOHn1t1A==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.16.tgz", + "integrity": "sha512-06m3u1WIT49iYLK2GJWdT7Lmx54pX8imcW06AFnmgMXYDQsTZDdNXpHM6vwwL29LAWDv44j8g+eDPjJ4UNNiCA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2589,13 +2587,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.14.tgz", - "integrity": "sha512-FM0qHSJ4Sqg49wBCcljq//J9V8SJbq3XFmjaWCF8Tk2hIuYkYZp7joXHs0Ld3FnM+9rj84OQTqSq8zczArNMNg==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.16.tgz", + "integrity": "sha512-q5FvwPYGHmDF4/J7ssWMBHKDRY/3ar1PNoKTMYh/1foSCJ2e/Hv/GTuc63h03xi12IRyTn8R/M/56vH6qd+rSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.14", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.14", + "@typescript-eslint/types": "8.0.0-alpha.16", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2641,15 +2639,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.14.tgz", - "integrity": "sha512-hiH1uqRVyOPd+ZWqInwRob2s3Cq+p7LTIolvj+x7QJ6CpBCPrEMEPVuBiFibw2/rW+zJGTa3Ggjdpqy8bLb60g==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.16.tgz", + "integrity": "sha512-u7mFyhJ4/jX7VaGieK+BC+PynvCH8fdr4Gie4RXO9bclvGAvMTzk62UZ65t90KN25M9/tvodxUoaZS4W4MQSNg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.14", - "@typescript-eslint/types": "8.0.0-alpha.14", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.14" + "@typescript-eslint/scope-manager": "8.0.0-alpha.16", + "@typescript-eslint/types": "8.0.0-alpha.16", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.16" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2663,12 +2661,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.14.tgz", - "integrity": "sha512-LwUhX8+ttlzJWhqLAkiH7E1tX2WJS0zvK0D83w4L9DRl4TRSQBuGtPIM1+GvG90VMix8sjlGaybBzWfNji1cUw==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.16.tgz", + "integrity": "sha512-vSmfkS6FVBW1lhuf700XjcbQXtoXg3Aqbi+axsFYPNr/6oEkpLRonbKMxBzj4cGTnL/3sJl+gDVQSS7fVHWz3A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.14", + "@typescript-eslint/types": "8.0.0-alpha.16", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2828,9 +2826,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2915,22 +2913,29 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "devOptional": true }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", "optional": true, "dependencies": { "delegates": "^1.0.0", @@ -3019,14 +3024,12 @@ } }, "node_modules/avvio": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz", - "integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.2.tgz", + "integrity": "sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==", "dev": true, "dependencies": { "@fastify/error": "^3.3.0", - "archy": "^1.0.0", - "debug": "^4.0.0", "fastq": "^1.17.1" } }, @@ -3203,12 +3206,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4546,9 +4549,9 @@ "dev": true }, "node_modules/fast-json-stringify": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.15.1.tgz", - "integrity": "sha512-JopGtkvvguRqrS4gHXSSA2jf4pDgOZkeBAkLO1LbzOpiOMo7/kugoR+KiWifpLpluaVeYDkAuxCJOj4Gyc6L9A==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.0.tgz", + "integrity": "sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==", "dev": true, "dependencies": { "@fastify/merge-json-schemas": "^0.1.0", @@ -4561,9 +4564,9 @@ } }, "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -4712,9 +4715,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4918,6 +4921,7 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "graceful-fs": "^4.1.2", @@ -4933,6 +4937,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -4953,6 +4958,7 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -4990,6 +4996,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", "optional": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -5108,16 +5115,16 @@ "dev": true }, "node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "devOptional": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.11.0" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -5190,9 +5197,9 @@ } }, "node_modules/globals": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.2.0.tgz", - "integrity": "sha512-FQ5YwCHZM3nCmtb5FzEWwdUc9K5d3V/w9mzcz8iGD1gC/aOTHc6PouYu0kkKipNJqHAT7m51sqzQjEjIP+cK0A==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", + "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", "dev": true, "engines": { "node": ">=18" @@ -5491,6 +5498,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5873,6 +5881,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5884,9 +5893,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", - "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -6351,9 +6360,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "devOptional": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -6610,9 +6619,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", - "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.2.tgz", + "integrity": "sha512-V9mGLlaXN1WETzqQvSu6qf6XVAr3nFuJvWsHcuzCCCo6xUKawwSxOPTpan5CGOSKTn5w/bQuCZcLPJkyysgC3w==", "dev": true }, "node_modules/light-my-request": { @@ -6833,18 +6842,30 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -6919,9 +6940,9 @@ } }, "node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "devOptional": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -7356,9 +7377,9 @@ } }, "node_modules/node-switchbot": { - "version": "2.1.1-beta.0", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.1-beta.0.tgz", - "integrity": "sha512-dfGWEwvxCgz+7aeqR7cr8/WOCKxE1W5MHplkXsIBK7XfSSnnWVzX1ct7Pu4JUKh/RCAnD9BqiMtdXUKr6/LvxA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.1.tgz", + "integrity": "sha512-QmjlroUt721Ik8Bq8AhqBqMMZgqXNF4jdbuGRpbLFOUcRXRmrecBVLlbhvXy++4FLMWaCzAuXMnpJ1qxv+IYDw==", "optional": true, "dependencies": { "@abandonware/noble": "^1.9.2-25" @@ -7368,9 +7389,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", - "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.1.tgz", + "integrity": "sha512-k43xGaDtaDIcufn0Fc6fTtsdKSkV/hQzoQFigNH//GaKta28yoKVYXCnV+KXRqfT/YzsFaQU9VdeEG+HEyxr6A==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -7914,9 +7935,9 @@ } }, "node_modules/npm-registry-fetch/node_modules/cacache/node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -8035,6 +8056,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", "optional": true, "dependencies": { "are-we-there-yet": "^2.0.0", @@ -8335,9 +8357,9 @@ } }, "node_modules/pacote/node_modules/cacache/node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -8528,12 +8550,12 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -8920,6 +8942,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", "dev": true, "dependencies": { "glob": "^10.2.2", @@ -8969,6 +8992,18 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -9781,9 +9816,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", "dev": true }, "node_modules/split": { @@ -10530,14 +10565,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.0.0-alpha.14", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.14.tgz", - "integrity": "sha512-Un2y0pbBCdvmk2YsY/S/oftSA/4tEZtRMfewHlXJ43LBR07V2HSXPC/t6RJ29KZ+N5ORqe61QUQLupquVBPZhQ==", + "version": "8.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.16.tgz", + "integrity": "sha512-hseQjFKLOZXuBjGgEoYWKD+EL1yd2nVvqL9TLq8RELE1ZGkha15WS98GfwpREZkak+CuTPNsRHHNxeXUesQ/DA==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.0.0-alpha.14", - "@typescript-eslint/parser": "8.0.0-alpha.14", - "@typescript-eslint/utils": "8.0.0-alpha.14" + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.16", + "@typescript-eslint/parser": "8.0.0-alpha.16", + "@typescript-eslint/utils": "8.0.0-alpha.16" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10571,9 +10606,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.17.0.tgz", - "integrity": "sha512-fs13QiDjPIzJ7gFAOal9CSG0c92rT2xw6MuMUJ4H30Eg5GCauLWYCCZA1tInjd6M4y+JZjVCCFr9pFpbhcC64w==", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz", + "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==", "engines": { "node": ">=18.17" } @@ -10750,14 +10785,14 @@ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, "node_modules/usb": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/usb/-/usb-2.12.1.tgz", - "integrity": "sha512-hgtoSQUFuMXVJBApelpUTiX7ZB83MQCbYeHTBsHftA2JG7YZ76ycwIgKQhkhKqVY76C8K6xJscHpF7Ep0eG3pQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/usb/-/usb-2.13.0.tgz", + "integrity": "sha512-pTNKyxD1DfC1DYu8kFcIdpE8f33e0c2Sbmmi0HEs28HTVC555uocvYR1g5DDv4CBibacCh4BqRyYZJylN4mBbw==", "hasInstallScript": true, "optional": true, "dependencies": { "@types/w3c-web-usb": "^1.0.6", - "node-addon-api": "^7.0.0", + "node-addon-api": "^8.0.0", "node-gyp-build": "^4.5.0" }, "engines": { @@ -10765,12 +10800,12 @@ } }, "node_modules/usb/node_modules/node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.0.0.tgz", + "integrity": "sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==", "optional": true, "engines": { - "node": "^16 || ^18 || >= 20" + "node": "^18 || ^20 || >= 21" } }, "node_modules/util-deprecate": { diff --git a/package.json b/package.json index 749be64a..a6665aad 100644 --- a/package.json +++ b/package.json @@ -82,10 +82,10 @@ "fakegato-history": "^0.6.4", "homebridge-lib": "^7.0.1", "rxjs": "^7.8.1", - "undici": "^6.17.0" + "undici": "^6.18.1" }, "optionalDependencies": { - "node-switchbot": "2.1.1-beta.0" + "node-switchbot": "2.1.1" }, "devDependencies": { "@eslint/js": "^9.3.0", @@ -93,10 +93,10 @@ "@types/eslint__js": "^8.42.3", "@types/node": "^20.12.12", "eslint": "^9.3.0", - "globals": "^15.2.0", + "globals": "^15.3.0", "homebridge": "^1.8.2", "homebridge-config-ui-x": "4.56.2", - "nodemon": "^3.1.0", + "nodemon": "^3.1.1", "npm-check-updates": "^16.14.20", "rimraf": "^5.0.7", "ts-node": "^10.9.2", diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index 38a58fc8..fbdbda3e 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -84,7 +84,7 @@ export class BlindTilt extends deviceBase { .setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name) .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ - minStep: Number(this.minStep(device)), + minStep: device.blindTilt?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -98,7 +98,7 @@ export class BlindTilt extends deviceBase { this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ - minStep: Number(this.minStep(device)), + minStep: device.blindTilt?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -189,33 +189,10 @@ export class BlindTilt extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { slidePosition, battery, lightLevel } = context; - const { CurrentPosition } = this.WindowCovering; - const { BatteryLevel } = this.Battery; - const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; - this.debugLog(`${device.deviceType}: ${accessory.displayName} (slidePosition, battery, lightLevel) = ` - + `Webhook:(${slidePosition}, ${battery}, ${lightLevel}), current:(${CurrentPosition}, ${BatteryLevel}, ${CurrentAmbientLightLevel})`); - this.WindowCovering.CurrentPosition = slidePosition; - this.Battery.BatteryLevel = battery; - if (!device.blindTilt?.hide_lightsensor) { - this.LightSensor!.CurrentAmbientLightLevel = lightLevel; - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // update slide progress interval(this.deviceUpdateRate * 1000) - //.pipe(skipWhile(() => this.blindTiltUpdateInProgress)) .subscribe(async () => { if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) { return; @@ -232,15 +209,15 @@ export class BlindTilt extends deviceBase { tap(() => { this.blindTiltUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`); + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.blindTiltUpdateInProgress = false; }); @@ -291,8 +268,8 @@ export class BlindTilt extends deviceBase { + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); if (!this.device.blindTilt?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.blindTilt?.set_minLux ?? 1; + const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001; const spaceBetweenLevels = 9; if (this.LightSensor?.CurrentAmbientLightLevel === 0) { @@ -307,10 +284,8 @@ export class BlindTilt extends deviceBase { break; case 2: this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + - ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`); break; case 3: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; @@ -345,10 +320,8 @@ export class BlindTilt extends deviceBase { this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; this.debugLog(); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + - ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // Battery this.Battery.BatteryLevel = Number(serviceData.battery); @@ -357,10 +330,8 @@ export class BlindTilt extends deviceBase { } else { this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } @@ -405,20 +376,16 @@ export class BlindTilt extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING` + ` PositionState: ${this.WindowCovering.PositionState}`); } else { - this.debugLog( - `${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby because reached position,` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby because reached position,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED` + ` PositionState: ${this.WindowCovering.PositionState}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition; if (homekitTiltAngle) { this.WindowCovering.TargetHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle; @@ -426,14 +393,11 @@ export class BlindTilt extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + - ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, - ); - + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); if (!this.device.blindTilt?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.blindTilt?.set_minLux ?? 1; + const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001; // Brightness switch (deviceStatus.body.brightness) { case 'dim': @@ -491,10 +455,8 @@ export class BlindTilt extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -555,10 +517,35 @@ export class BlindTilt extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { slidePosition, battery, lightLevel } = context; + const { CurrentPosition } = this.WindowCovering; + const { BatteryLevel } = this.Battery; + const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (slidePosition, battery, lightLevel) = Webhook:(${slidePosition}, ${battery},` + + ` ${lightLevel}), current:(${CurrentPosition}, ${BatteryLevel}, ${CurrentAmbientLightLevel})`); + this.WindowCovering.CurrentPosition = slidePosition; + this.Battery.BatteryLevel = battery; + if (!device.blindTilt?.hide_lightsensor) { + this.LightSensor!.CurrentAmbientLightLevel = lightLevel; + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -571,9 +558,8 @@ export class BlindTilt extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -615,10 +601,8 @@ export class BlindTilt extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { @@ -626,10 +610,8 @@ export class BlindTilt extends deviceBase { await this.BLEPushConnection(); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`); } } @@ -682,25 +664,21 @@ export class BlindTilt extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` - + ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}` + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle},` - + ` TargetPosition ${this.WindowCovering.TargetHorizontalTiltAngle}`, - ); + + ` TargetPosition ${this.WindowCovering.TargetHorizontalTiltAngle}`); } } @@ -747,24 +725,18 @@ export class BlindTilt extends deviceBase { || this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle) { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING; this.setNewTarget = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else if (this.WindowCovering.TargetPosition < this.WindowCovering.CurrentPosition) { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING; this.setNewTarget = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } else { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.setNewTarget = false; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` - + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`); } this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState); this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState); @@ -798,8 +770,7 @@ export class BlindTilt extends deviceBase { this.accessory.context.CurrentHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle; this.WindowCovering.Service.updateCharacteristic( this.hap.Characteristic.CurrentHorizontalTiltAngle, - Number(this.WindowCovering.CurrentHorizontalTiltAngle), - ); + Number(this.WindowCovering.CurrentHorizontalTiltAngle)); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`); } @@ -851,10 +822,8 @@ export class BlindTilt extends deviceBase { } this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } } // BatteryLevel @@ -955,36 +924,6 @@ export class BlindTilt extends deviceBase { } } - async minStep(device: device & devicesConfig): Promise { - let set_minStep: number; - if (device.blindTilt?.set_minStep) { - set_minStep = device.blindTilt?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - - async minLux(): Promise { - let set_minLux: number; - if (this.device.blindTilt?.set_minLux) { - set_minLux = this.device.blindTilt?.set_minLux; - } else { - set_minLux = 1; - } - return set_minLux; - } - - async maxLux(): Promise { - let set_maxLux: number; - if (this.device.blindTilt?.set_maxLux) { - set_maxLux = this.device.blindTilt?.set_maxLux; - } else { - set_maxLux = 6001; - } - return set_maxLux; - } - async offlineOff(): Promise { if (this.device.offline) { this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); @@ -1004,7 +943,7 @@ export class BlindTilt extends deviceBase { this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e); this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e); this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e); - if (!this.device.curtain?.hide_lightsensor) { + if (!this.device.blindTilt?.hide_lightsensor) { this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e); this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e); } diff --git a/src/device/bot.ts b/src/device/bot.ts index a0eebe4f..f710d4b6 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -469,30 +469,8 @@ export class Bot extends deviceBase { await this.refreshStatus(); }); - //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { power, battery, deviceMode } = context; - const { botMode } = this; - const On = await this.getOn(); - const { BatteryLevel } = this.Battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(power, battery, deviceMode) = ' + - `Webhook:(${power}, ${battery}, ${deviceMode}), ` + - `current:(${On}, ${BatteryLevel}, ${botMode})`); - await this.setOn(power); - this.Battery.BatteryLevel = battery; - this.botMode = deviceMode; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + // regisiter webhook event handler + this.registerWebhook(accessory, device); // Watch for Bot change events // We put in a debounce of 1000ms so we don't make duplicate calls @@ -501,7 +479,7 @@ export class Bot extends deviceBase { tap(() => { this.botUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { @@ -516,10 +494,8 @@ export class Bot extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.botUpdateInProgress = false; }); @@ -616,10 +592,8 @@ export class Bot extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -680,10 +654,33 @@ export class Bot extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { power, battery, deviceMode } = context; + const { botMode } = this; + const On = await this.getOn(); + const { BatteryLevel } = this.Battery; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (power, battery, deviceMode) = Webhook:(${power}, ${battery}, ${deviceMode}),` + + ` current:(${On}, ${BatteryLevel}, ${botMode})`); + await this.setOn(power); + this.Battery.BatteryLevel = battery; + this.botMode = deviceMode; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -703,9 +700,8 @@ export class Bot extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -753,10 +749,8 @@ export class Bot extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else if (this.botMode === 'switch') { @@ -839,7 +833,7 @@ export class Bot extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 4575b6e9..d73e7348 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -10,7 +10,7 @@ import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** @@ -78,7 +78,7 @@ export class CeilingLight extends deviceBase { this.accessory.configureController(this.AdaptiveLightingController); this.accessory.context.adaptiveLighting = true; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` - + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); @@ -95,7 +95,7 @@ export class CeilingLight extends deviceBase { this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ - minStep: this.minStep(device), + minStep: device.ceilinglight?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -158,48 +158,7 @@ export class CeilingLight extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { powerState, brightness, colorTemperature } = context; - const { On, Brightness, ColorTemperature } = this.LightBulb; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(powerState, brightness, colorTemperature) = ' + - `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), ` + - `current:(${On}, ${Brightness}, ${ColorTemperature})`); - - // On - this.LightBulb.On = powerState === 'ON' ? true : false; - if (this.accessory.context.Brightness !== this.LightBulb.On) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } - - // Brightness - this.LightBulb.Brightness = brightness; - if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } - - // ColorTemperature - this.LightBulb.ColorTemperature = colorTemperature; - if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Bulb change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -208,17 +167,15 @@ export class CeilingLight extends deviceBase { tap(() => { this.ceilingLightUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.ceilingLightUpdateInProgress = false; }); @@ -267,9 +224,8 @@ export class CeilingLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); // Hue this.LightBulb.Hue = hue; @@ -319,10 +275,8 @@ export class CeilingLight extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -383,10 +337,55 @@ export class CeilingLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { powerState, brightness, colorTemperature } = context; + const { On, Brightness, ColorTemperature } = this.LightBulb; + this.debugLog(`${device.deviceType}: ${accessory.displayName} ` + + '(powerState, brightness, colorTemperature) = ' + + `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), ` + + `current:(${On}, ${Brightness}, ${ColorTemperature})`); + + // On + this.LightBulb.On = powerState === 'ON' ? true : false; + if (accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness + this.LightBulb.Brightness = brightness; + if (accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } + + // ColorTemperature + this.LightBulb.ColorTemperature = colorTemperature; + if (accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -410,9 +409,8 @@ export class CeilingLight extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -461,17 +459,14 @@ export class CeilingLight extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` - + ` No BLEpushChanges: On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` No BLEpushChanges: On: ${this.LightBulb.On}, ` + + `OnCached: ${this.accessory.context.On}`); } } @@ -505,24 +500,20 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.LightBulb.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + + `On: ${this.LightBulb.On}, ` + + `OnCached: ${this.accessory.context.On}`); } // Push Hue & Saturation Update if (this.LightBulb.On) { @@ -566,23 +557,19 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, HueCached: ` + + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`); } } @@ -617,16 +604,12 @@ export class CeilingLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with` + + ` ${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + - `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); } } @@ -654,24 +637,20 @@ export class CeilingLight extends deviceBase { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + - `Brightness: ${this.LightBulb.Brightness}, ` + - `BrightnessCached: ${this.accessory.context.Brightness}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + + `Brightness: ${this.LightBulb.Brightness}, ` + + `BrightnessCached: ${this.accessory.context.Brightness}`); } } @@ -731,8 +710,8 @@ export class CeilingLight extends deviceBase { // Updating the hue/sat to the corresponding values mimics native adaptive lighting const hs = m2hs(value); - this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); - this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]); this.LightBulb.ColorTemperature = value; this.doCeilingLightUpdate.next(); @@ -750,7 +729,7 @@ export class CeilingLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`); } - this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); this.LightBulb.Hue = value; this.doCeilingLightUpdate.next(); @@ -768,7 +747,7 @@ export class CeilingLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`); } - this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); + this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140); this.LightBulb.Saturation = value; this.doCeilingLightUpdate.next(); @@ -839,16 +818,6 @@ export class CeilingLight extends deviceBase { } } - minStep(device: device & devicesConfig): number { - let set_minStep: number; - if (device.ceilinglight?.set_minStep) { - set_minStep = device.ceilinglight?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - async offlineOff(): Promise { if (this.device.offline) { this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index e5fe1917..7bf406c7 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -10,7 +10,7 @@ import { hs2rgb, rgb2hs, m2hs } from '../utils.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; -import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; +import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js'; import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** @@ -78,7 +78,7 @@ export class ColorBulb extends deviceBase { accessory.configureController(this.AdaptiveLightingController); accessory.context.adaptiveLighting = true; this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting},` - + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); } this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); @@ -94,7 +94,7 @@ export class ColorBulb extends deviceBase { this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ - minStep: this.minStep(device), + minStep: device.colorbulb?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -154,74 +154,7 @@ export class ColorBulb extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { powerState, brightness, color, colorTemperature } = context; - const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(powerState, brightness, color, colorTemperature) = ' + - `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + - `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); - - // On - this.LightBulb.On = powerState === 'ON' ? true : false; - if (this.accessory.context.Brightness !== this.LightBulb.On) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } - - // Brightness - this.LightBulb.Brightness = brightness; - if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } - - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); - const [red, green, blue] = color!.split(':'); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); - - const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`, - ); - - // Hue - this.LightBulb.Hue = hue; - if (this.accessory.context.Hue !== this.LightBulb.Hue) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); - } - - // Saturation - this.LightBulb.Saturation = saturation; - if (this.accessory.context.Saturation !== this.LightBulb.Saturation) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); - } - - this.LightBulb.ColorTemperature = colorTemperature; - if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Bulb change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -230,17 +163,15 @@ export class ColorBulb extends deviceBase { tap(() => { this.colorBulbUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.colorBulbUpdateInProgress = false; }); @@ -271,10 +202,8 @@ export class ColorBulb extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${serviceData.blue}`); const [hue, saturation] = rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`); // Hue this.LightBulb.Hue = hue; @@ -321,9 +250,8 @@ export class ColorBulb extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); // Hue this.LightBulb.Hue = hue; @@ -373,10 +301,8 @@ export class ColorBulb extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -437,10 +363,81 @@ export class ColorBulb extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { powerState, brightness, color, colorTemperature } = context; + const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; + this.debugLog(`${device.deviceType}: ${accessory.displayName} ` + + '(powerState, brightness, color, colorTemperature) = ' + + `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); + + // On + this.LightBulb.On = powerState === 'ON' ? true : false; + if (accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness + this.LightBulb.Brightness = brightness; + if (accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } + + this.debugLog(`${device.deviceType}: ${accessory.displayName} color: ${JSON.stringify(color)}`); + const [red, green, blue] = color!.split(':'); + this.debugLog(`${device.deviceType}: ${accessory.displayName} red: ${JSON.stringify(red)}`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} green: ${JSON.stringify(green)}`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} blue: ${JSON.stringify(blue)}`); + + const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); + this.debugLog( + `${device.deviceType}: ${accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); + + // Hue + this.LightBulb.Hue = hue; + if (accessory.context.Hue !== this.LightBulb.Hue) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } + + // Saturation + this.LightBulb.Saturation = saturation; + if (accessory.context.Saturation !== this.LightBulb.Saturation) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } + + this.LightBulb.ColorTemperature = colorTemperature; + if (accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugWarnLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -458,15 +455,14 @@ export class ColorBulb extends deviceBase { async pushChanges(): Promise { if (!this.device.enableCloudService && this.OpenAPI) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`); - /*} else if (this.BLE) { - await this.BLEpushChanges();*/ + } else if (this.BLE) { + await this.BLEpushChanges(); } else if (this.OpenAPI && this.platform.config.credentials?.token) { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -515,10 +511,8 @@ export class ColorBulb extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); // Push Brightness Update @@ -534,10 +528,8 @@ export class ColorBulb extends deviceBase { await this.BLEpushRGBChanges(); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` - + `On: ${this.LightBulb.On}, ` + `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, On: ${this.LightBulb.On},` + + ` OnCached: ${this.accessory.context.On}`); } } @@ -566,18 +558,13 @@ export class ColorBulb extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + - `Brightness: ${this.LightBulb.Brightness}, ` + - `BrightnessCached: ${this.accessory.context.Brightness}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + + `Brightness: ${this.LightBulb.Brightness}, BrightnessCached: ${this.accessory.context.Brightness}`); } } @@ -606,17 +593,13 @@ export class ColorBulb extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushColorTemperatureChanges with ` + - `${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushColorTemperatureChanges with ` + + `${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushColorTemperatureChanges.` + - `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushColorTemperatureChanges.` + + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`); } } @@ -651,17 +634,13 @@ export class ColorBulb extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, HueCached: ` + + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`); } } @@ -695,24 +674,19 @@ export class ColorBulb extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.LightBulb.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges,` + + `On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`); } // Push Hue & Saturation Update if (this.LightBulb.On) { @@ -763,16 +737,12 @@ export class ColorBulb extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, HueCached: ` + + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`); } } @@ -849,17 +819,12 @@ export class ColorBulb extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + - `Brightness: ${this.LightBulb.Brightness}, ` + - `BrightnessCached: ${this.accessory.context.Brightness}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` + + `Brightness: ${this.LightBulb.Brightness}, BrightnessCached: ${this.accessory.context.Brightness}`); } } @@ -1032,16 +997,6 @@ export class ColorBulb extends deviceBase { } } - minStep(device: device & devicesConfig): number { - let set_minStep: number; - if (device.colorbulb?.set_minStep) { - set_minStep = device.colorbulb?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - async offlineOff(): Promise { if (this.device.offline) { this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false); diff --git a/src/device/contact.ts b/src/device/contact.ts index 0708f484..43fc399b 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -167,35 +167,7 @@ export class Contact extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { detectionState, brightness, openState } = context; - const { ContactSensorState } = this.ContactSensor; - const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; - const { MotionDetected } = this.MotionSensor ?? {}; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(detectionState, brightness, openState) = ' + - `Webhook:(${detectionState}, ${brightness}, ${openState}), ` + - `current:(${MotionDetected}, ${CurrentAmbientLightLevel}, ${ContactSensorState})`); - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); - this.ContactSensor.ContactSensorState = openState === 'open' ? 1 : 0; - if (!this.device.contact?.hide_motionsensor) { - this.MotionSensor!.MotionDetected = detectionState === 'DETECTED' ? true : false; - } - if (!this.device.contact?.hide_lightsensor) { - this.LightSensor!.CurrentAmbientLightLevel = brightness === 'bright' ? set_maxLux : set_minLux; - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -230,8 +202,8 @@ export class Contact extends deviceBase { } // Light Level if (!this.device.contact?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.contact?.set_minLux ?? 1; + const set_maxLux = this.device.contact?.set_maxLux ?? 6001; switch (serviceData.lightLevel) { case true: this.LightSensor!.CurrentAmbientLightLevel = set_minLux; @@ -239,10 +211,8 @@ export class Contact extends deviceBase { default: this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + - ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); if (this.LightSensor!.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName}` + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); @@ -258,10 +228,8 @@ export class Contact extends deviceBase { } else { this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, ` - + `StatusLowBattery: ${this.Battery.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, ` + + `StatusLowBattery: ${this.Battery.StatusLowBattery}`); } async openAPIparseStatus(deviceStatus: deviceStatus): Promise { @@ -283,8 +251,8 @@ export class Contact extends deviceBase { } // Light Level if (!this.device.contact?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.contact?.set_minLux ?? 1; + const set_maxLux = this.device.contact?.set_maxLux ?? 6001; switch (deviceStatus.body.brightness) { case 'dim': this.LightSensor!.CurrentAmbientLightLevel = set_minLux; @@ -337,10 +305,8 @@ export class Contact extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -404,10 +370,39 @@ export class Contact extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { detectionState, brightness, openState } = context; + const { ContactSensorState } = this.ContactSensor; + const { CurrentAmbientLightLevel } = this.LightSensor ?? {}; + const { MotionDetected } = this.MotionSensor ?? {}; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (detectionState, brightness, openState) = Webhook:(${detectionState}, ` + + `${brightness}, ${openState}), current:(${MotionDetected}, ${CurrentAmbientLightLevel}, ${ContactSensorState})`); + const set_minLux = this.device.contact?.set_minLux ?? 1; + const set_maxLux = this.device.contact?.set_maxLux ?? 6001; + this.ContactSensor.ContactSensorState = openState === 'open' ? 1 : 0; + if (!device.contact?.hide_motionsensor) { + this.MotionSensor!.MotionDetected = detectionState === 'DETECTED' ? true : false; + } + if (!device.contact?.hide_lightsensor) { + this.LightSensor!.CurrentAmbientLightLevel = brightness === 'bright' ? set_maxLux : set_minLux; + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -436,14 +431,12 @@ export class Contact extends deviceBase { if (!this.device.contact?.hide_lightsensor) { if (this.LightSensor?.CurrentAmbientLightLevel === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); + + ` CurrentAmbientLightLevel: ${this.LightSensor?.CurrentAmbientLightLevel}`); } else { this.accessory.context.CurrentAmbientLightLevel = this.LightSensor.CurrentAmbientLightLevel; this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`); } } if (this.Battery.BatteryLevel === undefined) { @@ -472,26 +465,6 @@ export class Contact extends deviceBase { } } - async minLux(): Promise { - let set_minLux: number; - if (this.device.contact?.set_minLux) { - set_minLux = this.device.contact!.set_minLux!; - } else { - set_minLux = 1; - } - return set_minLux; - } - - async maxLux(): Promise { - let set_maxLux: number; - if (this.device.contact?.set_maxLux) { - set_maxLux = this.device.contact!.set_maxLux!; - } else { - set_maxLux = 6001; - } - return set_maxLux; - } - async offlineOff(): Promise { if (this.device.offline) { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 89562c88..56ae75c9 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -10,7 +10,7 @@ import { Devices } from '../settings.js'; import { debounceTime, skipWhile, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge'; export class Curtain extends deviceBase { @@ -84,7 +84,7 @@ export class Curtain extends deviceBase { this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.CurrentPosition) .setProps({ - minStep: Number(this.minStep(device)), + minStep: device.curtain?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -97,7 +97,7 @@ export class Curtain extends deviceBase { this.WindowCovering.Service .getCharacteristic(this.hap.Characteristic.TargetPosition) .setProps({ - minStep: Number(this.minStep(device)), + minStep: device.curtain?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -187,25 +187,7 @@ export class Curtain extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { slidePosition, battery } = context; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(slidePosition, battery) = ' + - `Webhook:(${slidePosition}, ${battery}), ` + - `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`); - this.WindowCovering.CurrentPosition = slidePosition; - this.Battery.BatteryLevel = battery; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // update slide progress interval(this.deviceUpdateRate * 1000) @@ -226,23 +208,21 @@ export class Curtain extends deviceBase { tap(() => { this.curtainUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.curtainUpdateInProgress = false; }); // Setup EVE history features - this.setupHistoryService(device); + this.setupHistoryService(accessory, device); } private history(device: device & devicesConfig) { @@ -258,7 +238,7 @@ export class Curtain extends deviceBase { /* * Setup EVE history features for curtain devices. */ - async setupHistoryService(device: device & devicesConfig): Promise { + async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise { if (device.history !== true) { return; } @@ -293,8 +273,7 @@ export class Curtain extends deviceBase { this.accessory.context.lastActivation = entry.time; sensor?.updateCharacteristic( this.platform.eve.Characteristics.LastActivation, - Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()), - ); + Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime())); this.historyService.addEntry(entry); } }); @@ -350,14 +329,12 @@ export class Curtain extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + - ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); if (!this.device.curtain?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.curtain?.set_minLux ?? 1; + const set_maxLux = this.device.curtain?.set_maxLux ?? 6001; const spaceBetweenLevels = 9; // Brightness @@ -369,9 +346,8 @@ export class Curtain extends deviceBase { case 2: this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + - ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`, - ); + `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`); break; case 3: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; @@ -413,10 +389,8 @@ export class Curtain extends deviceBase { this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; this.debugLog(); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + - ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } // Battery this.Battery.BatteryLevel = Number(serviceData.battery); @@ -467,15 +441,13 @@ export class Curtain extends deviceBase { this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED; this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`); } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + - ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},` + + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`); // Brightness if (!this.device.curtain?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.curtain?.set_minLux ?? 1; + const set_maxLux = this.device.curtain?.set_maxLux ?? 6001; switch (deviceStatus.body.brightness) { case 'bright': this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; @@ -527,10 +499,8 @@ export class Curtain extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -591,10 +561,30 @@ export class Curtain extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { slidePosition, battery } = context; + this.debugLog(`${device.deviceType}: ${accessory.displayName} ` + + '(slidePosition, battery) = ' + + `Webhook:(${slidePosition}, ${battery}), ` + + `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`); + this.WindowCovering.CurrentPosition = slidePosition; + this.Battery.BatteryLevel = battery; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; } } @@ -607,9 +597,8 @@ export class Curtain extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -650,10 +639,8 @@ export class Curtain extends deviceBase { + `TargetPosition: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`); } catch (e) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`); await this.BLEPushConnection(); throw new Error('Connection error'); } @@ -662,10 +649,8 @@ export class Curtain extends deviceBase { await this.BLEPushConnection(); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`); } } @@ -717,16 +702,12 @@ export class Curtain extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + - ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` + + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`); } } @@ -850,10 +831,8 @@ export class Curtain extends deviceBase { } this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); if (this.device.history) { this.historyService?.addEntry({ time: Math.round(new Date().valueOf() / 1000), @@ -916,10 +895,10 @@ export class Curtain extends deviceBase { let setPositionMode: number; let Mode: string; if (Number(this.WindowCovering.TargetPosition) > 50) { - if (this.device.blindTilt?.setOpenMode === '1') { + if (this.device.curtain?.setOpenMode === '1') { setPositionMode = 1; Mode = 'Silent Mode'; - } else if (this.device.blindTilt?.setOpenMode === '0') { + } else if (this.device.curtain?.setOpenMode === '0') { setPositionMode = 0; Mode = 'Performance Mode'; } else { @@ -927,10 +906,10 @@ export class Curtain extends deviceBase { Mode = 'Default Mode'; } } else { - if (this.device.blindTilt?.setCloseMode === '1') { + if (this.device.curtain?.setCloseMode === '1') { setPositionMode = 1; Mode = 'Silent Mode'; - } else if (this.device.blindTilt?.setOpenMode === '0') { + } else if (this.device.curtain?.setOpenMode === '0') { setPositionMode = 0; Mode = 'Performance Mode'; } else { @@ -959,36 +938,6 @@ export class Curtain extends deviceBase { } } - async minStep(device: device & devicesConfig): Promise { - let set_minStep: number; - if (device.curtain?.set_minStep) { - set_minStep = device.curtain?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - - async minLux(): Promise { - let set_minLux: number; - if (this.device.curtain?.set_minLux) { - set_minLux = this.device.curtain?.set_minLux; - } else { - set_minLux = 1; - } - return set_minLux; - } - - async maxLux(): Promise { - let set_maxLux: number; - if (this.device.curtain?.set_maxLux) { - set_maxLux = this.device.curtain?.set_maxLux; - } else { - set_maxLux = 6001; - } - return set_maxLux; - } - async offlineOff(): Promise { if (this.device.offline) { this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100); diff --git a/src/device/device.ts b/src/device/device.ts index 8e40fc82..90d8b1b6 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -20,8 +20,9 @@ export abstract class deviceBase { // Config protected deviceLogging!: string; - protected deviceUpdateRate!: number; protected deviceRefreshRate!: number; + protected deviceUpdateRate!: number; + protected devicePushRate!: number; protected deviceMaxRetries!: number; protected deviceDelayBetweenRetries!: number; @@ -56,73 +57,95 @@ export abstract class deviceBase { this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI'; this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI'; - this.getDeviceLogSettings(device); - this.getDeviceRefreshRateSettings(device); - this.getDeviceRetry(device); - this.getDeviceConfigSettings(device); + this.getDeviceLogSettings(accessory, device); + this.getDeviceRateSettings(accessory, device); + this.getDeviceRetry(accessory, device); + this.getDeviceConfigSettings(accessory, device); this.getDeviceContext(accessory, device); - this.setupMqtt(device); - this.scan(device); + this.setupMqtt(accessory, device); + this.scan(accessory, device); // Set accessory information accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Name, device.deviceName) - .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName) - .setCharacteristic(this.hap.Characteristic.Model, device.model!) + .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760') + .setCharacteristic(this.hap.Characteristic.Name, device.deviceName ?? accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName) + .setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model) + .setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId) .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId); } - async getDeviceLogSettings(device: device & devicesConfig): Promise { + async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise { if (this.platform.debugMode) { - this.deviceLogging = this.accessory.context.logging = 'debugMode'; - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); + this.deviceLogging = accessory.context.logging = 'debugMode'; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`); } else if (device.logging) { - this.deviceLogging = this.accessory.context.logging = device.logging; - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); + this.deviceLogging = accessory.context.logging = device.logging; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`); } else if (this.config.logging) { - this.deviceLogging = this.accessory.context.logging = this.config.logging; - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); + this.deviceLogging = accessory.context.logging = this.config.logging; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`); } else { - this.deviceLogging = this.accessory.context.logging = 'standard'; - this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); + this.deviceLogging = accessory.context.logging = 'standard'; + this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`); } } - async getDeviceRefreshRateSettings(device: device & devicesConfig): Promise { + async getDeviceRateSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise { // refreshRate if (device.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); - } else if (this.platform.config.options!.refreshRate) { - this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); + this.deviceRefreshRate = device.refreshRate; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`); + } else if (this.config.options?.refreshRate) { + this.deviceRefreshRate = this.config.options.refreshRate; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`); + } else { + this.deviceRefreshRate = 5; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default refreshRate: ${this.deviceRefreshRate}`); } + accessory.context.deviceRefreshRate = this.deviceRefreshRate; // updateRate if (device.updateRate) { this.deviceUpdateRate = device.updateRate; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config updateRate: ${this.deviceUpdateRate}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config updateRate: ${this.deviceUpdateRate}`); + } else if (this.config.options?.updateRate) { + this.deviceUpdateRate = this.config.options.updateRate; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config updateRate: ${this.deviceUpdateRate}`); } else { this.deviceUpdateRate = 5; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default updateRate: ${this.deviceUpdateRate}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default updateRate: ${this.deviceUpdateRate}`); + } + accessory.context.deviceUpdateRate = this.deviceUpdateRate; + // pushRate + if (device.pushRate) { + this.devicePushRate = device.pushRate; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config pushRate: ${this.deviceUpdateRate}`); + } else if (this.config.options?.pushRate) { + this.devicePushRate = this.config.options.pushRate; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config pushRate: ${this.deviceUpdateRate}`); + } else { + this.devicePushRate = 1; + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default pushRate: ${this.deviceUpdateRate}`); } + accessory.context.devicePushRate = this.devicePushRate; } - async getDeviceRetry(device: device & devicesConfig): Promise { + async getDeviceRetry(accessory: PlatformAccessory, device: device & devicesConfig): Promise { if (device.maxRetries) { this.deviceMaxRetries = device.maxRetries; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Max Retries: ${this.deviceMaxRetries}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Max Retries: ${this.deviceMaxRetries}`); } else { this.deviceMaxRetries = 5; // Maximum number of retries - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Max Retries Not Set, Using: ${this.deviceMaxRetries}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Max Retries Not Set, Using: ${this.deviceMaxRetries}`); } if (device.delayBetweenRetries) { this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`); } else { this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Delay Between Retries Not Set,` + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Delay Between Retries Not Set,` + ` Using: ${this.deviceDelayBetweenRetries}`); } } @@ -147,36 +170,34 @@ export abstract class deviceBase { } } - - async scan(device: device & devicesConfig): Promise { + async scan(accessory: PlatformAccessory, device: device & devicesConfig): Promise { if (device.scanDuration) { if (this.deviceUpdateRate > device.scanDuration) { this.scanDuration = this.deviceUpdateRate; if (this.BLE) { this.warnLog( - `${this.device.deviceType}: ` + - `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`, - ); + `${this.device.deviceType}: ` + + `${accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`); } } else { - this.scanDuration = this.accessory.context.scanDuration = device.scanDuration; + this.scanDuration = accessory.context.scanDuration = device.scanDuration; } if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`); } } else { if (this.deviceUpdateRate > 1) { this.scanDuration = this.deviceUpdateRate; } else { - this.scanDuration = this.accessory.context.scanDuration = 1; + this.scanDuration = accessory.context.scanDuration = 1; } if (this.BLE) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default scanDuration: ${this.scanDuration}`); } } } - async getDeviceConfigSettings(device: device & devicesConfig): Promise { + async getDeviceConfigSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise { const deviceConfig = {}; if (device.logging !== 'standard') { deviceConfig['logging'] = device.logging; @@ -280,7 +301,7 @@ export abstract class deviceBase { const config = Object.assign({}, deviceConfig, botConfig, curtainConfig, waterdetectorConfig, striplightConfig, plugConfig, iosensorConfig, meterConfig, humidifierConfig, hubConfig, lockConfig, ceilinglightConfig, colorbulbConfig, contactConfig, motionConfig, blindTiltConfig); if (Object.entries(config).length !== 0) { - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`); + this.debugSuccessLog(`${this.device.deviceType}: ${accessory.displayName} Config: ${JSON.stringify(config)}`); } } @@ -311,18 +332,18 @@ export abstract class deviceBase { /* * Setup MQTT hadler if URL is specified. */ - async setupMqtt(device: device & devicesConfig): Promise { + async setupMqtt(accessory: PlatformAccessory, device: device & devicesConfig): Promise { if (device.mqttURL) { try { const { connectAsync } = asyncmqtt; this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {}); this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`); this.mqttClient.on('error', (e: Error) => { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`); + this.errorLog(`${this.device.deviceType}: ${accessory.displayName} Failed to publish MQTT messages. ${e}`); }); } catch (e) { this.mqttClient = null; - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`); + this.errorLog(`${this.device.deviceType}: ${accessory.displayName} Failed to establish MQTT connection. ${e}`); } } } @@ -330,13 +351,13 @@ export abstract class deviceBase { /* * Setup EVE history graph feature if enabled. */ - async setupHistoryService(device: device & devicesConfig): Promise { + async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise { const mac = this.device .deviceId!.match(/.{1,2}/g)! .join(':') .toLowerCase(); this.historyService = device.history - ? new this.platform.fakegatoAPI('room', this.accessory, { + ? new this.platform.fakegatoAPI('room', accessory, { log: this.platform.log, storage: 'fs', filename: `${hostname().split('.')[0]}_${mac}_persist.json`, @@ -367,202 +388,201 @@ export abstract class deviceBase { case 'Humidifier': device.model = SwitchBotModel.Humidifier; device.bleModel = SwitchBotBLEModel.Humidifier; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Hub Mini': device.model = SwitchBotModel.HubMini; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Hub Plus': device.model = SwitchBotModel.HubPlus; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Hub 2': device.model = SwitchBotModel.Hub2; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Bot': device.model = SwitchBotModel.Bot; device.bleModel = SwitchBotBLEModel.Bot; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Meter': device.model = SwitchBotModel.Meter; device.bleModel = SwitchBotBLEModel.Meter; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'MeterPlus': device.model = SwitchBotModel.MeterPlusUS; device.bleModel = SwitchBotBLEModel.MeterPlus; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Meter Plus (JP)': device.model = SwitchBotModel.MeterPlusJP; device.bleModel = SwitchBotBLEModel.MeterPlus; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'WoIOSensor': device.model = SwitchBotModel.OutdoorMeter; device.bleModel = SwitchBotBLEModel.OutdoorMeter; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Water Detector': device.model = SwitchBotModel.WaterDetector; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Motion Sensor': device.model = SwitchBotModel.MotionSensor; device.bleModel = SwitchBotBLEModel.MotionSensor; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Contact Sensor': device.model = SwitchBotModel.ContactSensor; device.bleModel = SwitchBotBLEModel.ContactSensor; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Curtain': device.model = SwitchBotModel.Curtain; device.bleModel = SwitchBotBLEModel.Curtain; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Curtain3': device.model = SwitchBotModel.Curtain3; device.bleModel = SwitchBotBLEModel.Curtain3; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Blind Tilt': device.model = SwitchBotModel.BlindTilt; device.bleModel = SwitchBotBLEModel.BlindTilt; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Plug': device.model = SwitchBotModel.Plug; device.bleModel = SwitchBotBLEModel.PlugMiniUS; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Plug Mini (US)': device.model = SwitchBotModel.PlugMiniUS; device.bleModel = SwitchBotBLEModel.PlugMiniUS; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Plug Mini (JP)': device.model = SwitchBotModel.PlugMiniJP; device.bleModel = SwitchBotBLEModel.PlugMiniJP; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Smart Lock': device.model = SwitchBotModel.Lock; device.bleModel = SwitchBotBLEModel.Lock; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Smart Lock Pro': device.model = SwitchBotModel.LockPro; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Color Bulb': device.model = SwitchBotModel.ColorBulb; device.bleModel = SwitchBotBLEModel.ColorBulb; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'K10+': device.model = SwitchBotModel.K10; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'WoSweeper': device.model = SwitchBotModel.WoSweeper; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'WoSweeperMini': device.model = SwitchBotModel.WoSweeperMini; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Robot Vacuum Cleaner S1': device.model = SwitchBotModel.RobotVacuumCleanerS1; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Robot Vacuum Cleaner S1 Plus': device.model = SwitchBotModel.RobotVacuumCleanerS1Plus; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Robot Vacuum Cleaner S10': device.model = SwitchBotModel.RobotVacuumCleanerS10; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Ceiling Light': device.model = SwitchBotModel.CeilingLight; device.bleModel = SwitchBotBLEModel.CeilingLight; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Ceiling Light Pro': device.model = SwitchBotModel.CeilingLightPro; device.bleModel = SwitchBotBLEModel.CeilingLightPro; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Strip Light': device.model = SwitchBotModel.StripLight; device.bleModel = SwitchBotBLEModel.StripLight; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Indoor Cam': device.model = SwitchBotModel.IndoorCam; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Remote': device.model = SwitchBotModel.Remote; device.bleModel = SwitchBotBLEModel.Remote; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'remote with screen+': device.model = SwitchBotModel.UniversalRemote; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; case 'Battery Circulator Fan': device.model = SwitchBotModel.BatteryCirculatorFan; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); break; default: device.model = SwitchBotModel.Unknown; device.bleModel = SwitchBotBLEModel.Unknown; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`); } accessory.context.model = device.model; + // Firmware Version let deviceFirmwareVersion: string; if (device.firmware) { deviceFirmwareVersion = device.firmware; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); } else if (device.version) { deviceFirmwareVersion = device.version; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 FirmwareRevision: ${device.version}`); + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 2 FirmwareRevision: ${device.version}`); } else if (accessory.context.deviceVersion) { deviceFirmwareVersion = accessory.context.deviceVersion; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`); + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`); } else { deviceFirmwareVersion = this.platform.version ?? '0.0.0'; if (this.platform.version) { - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`); + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`); } else { - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`); } } - - // Firmware Version const version = deviceFirmwareVersion.toString(); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); let deviceVersion: string; if (version?.includes('.') === false) { const replace = version?.replace(/^V|-.*$/g, ''); @@ -572,14 +592,15 @@ export abstract class deviceBase { } else { deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; } - this.accessory + accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.SoftwareRevision, deviceVersion) .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(deviceVersion); - this.accessory.context.deviceVersion = deviceVersion; - this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`); + accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} deviceVersion: ${accessory.context.deviceVersion}`); } async statusCode(statusCode: number): Promise { @@ -588,14 +609,14 @@ export abstract class deviceBase { if (this.device.hubDeviceId === this.device.deviceId) { statusCode = 161; this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: ` - + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` - + `${this.device.deviceId}, meaning the device is it's own hub.`); + + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` + + `${this.device.deviceId}, meaning the device is it's own hub.`); } if (this.device.hubDeviceId === '000000000000') { statusCode = 161; this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: ` - + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` - + `${this.device.deviceId}, meaning the device is it's own hub.`); + + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: ` + + `${this.device.deviceId}, meaning the device is it's own hub.`); } } switch (statusCode) { @@ -612,12 +633,12 @@ export abstract class deviceBase { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); break; case 171: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + + `Hub: ${this.device.hubDeviceId}`); break; case 190: - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + - ` server, Or command format is invalid, statusCode: ${statusCode}`); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + + ` server, Or command format is invalid, statusCode: ${statusCode}`); break; case 100: this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); diff --git a/src/device/fan.ts b/src/device/fan.ts index 35dac785..1b46e237 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -9,7 +9,7 @@ import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; export class Fan extends deviceBase { // Services @@ -29,9 +29,16 @@ export class Fan extends deviceBase { ChargingState: CharacteristicValue; }; + private LightBulb: { + Name: CharacteristicValue; + Service: Service; + On: CharacteristicValue; + Brightness: CharacteristicValue; + }; + // Updates - plugUpdateInProgress!: boolean; - doPlugUpdate!: Subject; + fanUpdateInProgress!: boolean; + doFanUpdate!: Subject; constructor( readonly platform: SwitchBotPlatform, @@ -40,8 +47,8 @@ export class Fan extends deviceBase { ) { super(platform, accessory, device); // this is subject we use to track when we need to POST changes to the SwitchBot API - this.doPlugUpdate = new Subject(); - this.plugUpdateInProgress = false; + this.doFanUpdate = new Subject(); + this.fanUpdateInProgress = false; // Initialize Fan Service accessory.context.Fan = accessory.context.Fan ?? {}; @@ -111,6 +118,33 @@ export class Fan extends deviceBase { return this.Battery.StatusLowBattery; }); + // Initialize LightBulb Service + accessory.context.LightBulb = accessory.context.LightBulb ?? {}; + this.LightBulb = { + Name: accessory.context.LightBulb.Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service, + On: accessory.context.On ?? false, + Brightness: accessory.context.Brightness ?? 0, + }; + accessory.context.LightBulb = this.LightBulb as object; + + // Initialize LightBulb Characteristics + this.LightBulb.Service + .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name) + .getCharacteristic(this.hap.Characteristic.On) + .onGet(() => { + return this.LightBulb.On; + }) + .onSet(this.OnSet.bind(this)); + + // Initialize LightBulb Brightness Characteristic + this.LightBulb.Service + .getCharacteristic(this.hap.Characteristic.Brightness) + .onGet(() => { + return this.LightBulb.Brightness; + }) + .onSet(this.BrightnessSet.bind(this)); + // Retrieve initial values and updateHomekit this.refreshStatus(); @@ -119,80 +153,32 @@ export class Fan extends deviceBase { // Start an update interval interval(this.deviceRefreshRate * 1000) - .pipe(skipWhile(() => this.plugUpdateInProgress)) + .pipe(skipWhile(() => this.fanUpdateInProgress)) .subscribe(async () => { await this.refreshStatus(); }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { version, battery, powerState, oscillation, chargingStatus, fanSpeed } = context; - const { Active, SwingMode, RotationSpeed } = this.Fan; - const { BatteryLevel, ChargingState } = this.Battery; - const { FirmwareRevision } = this.accessory.context; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(version, battery, powerState, oscillation, chargingStatus, fanSpeed) = ' - + `Webhook:(${version}, ${battery}, ${powerState}, ${oscillation}, ${chargingStatus}, ${fanSpeed}), ` - + `current:(${FirmwareRevision}, ${BatteryLevel}, ${Active}, ${SwingMode}, ${ChargingState}, ${RotationSpeed})`); - - // Active - this.Fan.Active = powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE; - - // SwingMode - this.Fan.SwingMode = oscillation === 'on' ? - this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED; - - // RotationSpeed - this.Fan.RotationSpeed = fanSpeed; - - // ChargingState - this.Battery.ChargingState = chargingStatus === 'charging' ? - this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; - - // BatteryLevel - this.Battery.BatteryLevel = battery; - - // Firmware Version - if (version) { - this.accessory.context.version = version; - this.accessory - .getService(this.hap.Service.AccessoryInformation)! - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, this.accessory.context.version) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(this.accessory.context.version); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Plug change events // We put in a debounce of 100ms so we don't make duplicate calls - this.doPlugUpdate + this.doFanUpdate .pipe( tap(() => { - this.plugUpdateInProgress = true; + this.fanUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } - this.plugUpdateInProgress = false; + this.fanUpdateInProgress = false; }); } @@ -271,10 +257,8 @@ export class Fan extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -335,10 +319,58 @@ export class Fan extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { version, battery, powerState, oscillation, chargingStatus, fanSpeed } = context; + const { Active, SwingMode, RotationSpeed } = this.Fan; + const { BatteryLevel, ChargingState } = this.Battery; + const { FirmwareRevision } = accessory.context; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (version, battery, powerState, oscillation, chargingStatus, fanSpeed) = ` + + `Webhook:(${version}, ${battery}, ${powerState}, ${oscillation}, ${chargingStatus}, ${fanSpeed}), ` + + `current:(${FirmwareRevision}, ${BatteryLevel}, ${Active}, ${SwingMode}, ${ChargingState}, ${RotationSpeed})`); + + // Active + this.Fan.Active = powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE; + + // SwingMode + this.Fan.SwingMode = oscillation === 'on' ? + this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED; + + // RotationSpeed + this.Fan.RotationSpeed = fanSpeed; + + // ChargingState + this.Battery.ChargingState = chargingStatus === 'charging' ? + this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; + + // BatteryLevel + this.Battery.BatteryLevel = battery; + + // Firmware Version + if (version) { + accessory.context.version = version; + accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(accessory.context.version); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -361,13 +393,12 @@ export class Fan extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) - .pipe(skipWhile(() => this.plugUpdateInProgress)) + .pipe(skipWhile(() => this.fanUpdateInProgress)) .pipe(take(1)) .subscribe(async () => { await this.refreshStatus(); @@ -412,10 +443,8 @@ export class Fan extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { @@ -468,6 +497,100 @@ export class Fan extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + `On: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`); } + // Push RotationSpeed Update + if (this.Fan.Active) { + await this.pushRotationSpeedChanges(); + } + // Push SwingMode Update + if (this.Fan.Active) { + await this.pushSwingModeChanges(); + } + } + + async pushRotationSpeedChanges(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushRotationSpeedChanges`); + if (this.Fan.SwingMode !== this.accessory.context.SwingMode) { + const bodyChange = JSON.stringify({ + command: 'setWindSpeed', + parameter: `${this.Fan.RotationSpeed}`, + commandType: 'command', + }); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); + try { + const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { + body: bodyChange, + method: 'POST', + headers: this.platform.generateHeaders(), + }); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); + const deviceStatus: any = await body.json(); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + } else { + this.statusCode(statusCode); + this.statusCode(deviceStatus.statusCode); + } + } catch (e: any) { + this.apiError(e); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushRotationSpeedChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, pushRotationSpeedChanges: ${this.Fan.RotationSpeed}, ` + + `pushRotationSpeedChangesCached: ${this.accessory.context.RotationSpeed}`); + } + } + + async pushSwingModeChanges(): Promise { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushSwingModeChanges`); + if (this.Fan.SwingMode !== this.accessory.context.SwingMode) { + let parameter = ''; + if (this.Fan.SwingMode === this.hap.Characteristic.SwingMode.SWING_ENABLED) { + parameter = 'on'; + } else { + parameter = 'off'; + } + const bodyChange = JSON.stringify({ + command: 'setOscillation', + parameter: `${parameter}`, + commandType: 'command', + }); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`); + try { + const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, { + body: bodyChange, + method: 'POST', + headers: this.platform.generateHeaders(), + }); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); + const deviceStatus: any = await body.json(); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`); + if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { + this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); + this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + } else { + this.statusCode(statusCode); + this.statusCode(deviceStatus.statusCode); + } + } catch (e: any) { + this.apiError(e); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushSwingModeChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, SwingMode: ${this.Fan.SwingMode}, ` + + `SwingModeCached: ${this.accessory.context.SwingMode}`); + } } /** @@ -481,7 +604,7 @@ export class Fan extends deviceBase { } this.Fan.Active = value; - this.doPlugUpdate.next(); + this.doFanUpdate.next(); } /** @@ -495,7 +618,7 @@ export class Fan extends deviceBase { } this.Fan.RotationSpeed = value; - this.doPlugUpdate.next(); + this.doFanUpdate.next(); } /** @@ -509,7 +632,37 @@ export class Fan extends deviceBase { } this.Fan.SwingMode = value; - this.doPlugUpdate.next(); + this.doFanUpdate.next(); + } + + /** + * Handle requests to set the value of the "On" characteristic + */ + async OnSet(value: CharacteristicValue): Promise { + if (this.LightBulb.On === this.accessory.context.On) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`); + } else { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`); + } + + this.LightBulb.On = value; + this.doFanUpdate.next(); + } + + /** + * Handle requests to set the value of the "Brightness" characteristic + */ + async BrightnessSet(value: CharacteristicValue): Promise { + if (this.LightBulb.Brightness === this.accessory.context.Brightness) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`); + } else if (this.LightBulb.On) { + this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); + } else { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`); + } + + this.LightBulb.Brightness = value; + this.doFanUpdate.next(); } async updateHomeKitCharacteristics(): Promise { diff --git a/src/device/hub.ts b/src/device/hub.ts index 1808a597..94b4bff0 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -148,42 +148,8 @@ export class Hub extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { temperature, humidity, lightLevel } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - if (context.scale !== 'CELCIUS' && device.hub?.convertUnitTo === undefined) { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` - + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`); - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(scale, temperature, humidity, lightLevel) = ' + - `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); - if (!this.device.hub?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (!this.device.hub?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.hub?.convertUnitTo); - } - if (!this.device.hub?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); - const spaceBetweenLevels = 19; - await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); + } async openAPIparseStatus(deviceStatus: deviceStatus): Promise { @@ -203,15 +169,13 @@ export class Hub extends deviceBase { // Brightness if (!this.device.hub?.hide_lightsensor) { if (!this.device.curtain?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.curtain?.set_minLux ?? 1; + const set_maxLux = this.device.curtain?.set_maxLux ?? 6001; const spaceBetweenLevels = 19; const lightLevel = deviceStatus.body.lightLevel; await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${deviceStatus.body.lightLevel},` + - ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${deviceStatus.body.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } if (!this.device.hub?.hide_lightsensor) { this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); @@ -249,9 +213,7 @@ export class Hub extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`); try { const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, - `${Devices}/${this.device.deviceId}/status`, { - headers: this.platform.generateHeaders(), - }); + `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`); const deviceStatus: any = await body.json(); this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`); @@ -259,7 +221,6 @@ export class Hub extends deviceBase { if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) { this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); - this.openAPIparseStatus(deviceStatus); this.updateHomeKitCharacteristics(); } else { @@ -268,10 +229,47 @@ export class Hub extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { + try { + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { temperature, humidity, lightLevel } = context; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.hub?.convertUnitTo === undefined) { + this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` + + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`); + } + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + '(scale, temperature, humidity, lightLevel) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`); + if (!this.device.hub?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (!this.device.hub?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.hub?.convertUnitTo); + } + if (!this.device.hub?.hide_lightsensor) { + const set_minLux = this.device.curtain?.set_minLux ?? 1; + const set_maxLux = this.device.curtain?.set_maxLux ?? 6001; + const spaceBetweenLevels = 19; + await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; } } @@ -335,10 +333,8 @@ export class Hub extends deviceBase { } this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} ` - + `updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + + `updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } } @@ -382,26 +378,6 @@ export class Hub extends deviceBase { } } - async minLux(): Promise { - let set_minLux: number; - if (this.device.curtain?.set_minLux) { - set_minLux = this.device.curtain?.set_minLux; - } else { - set_minLux = 1; - } - return set_minLux; - } - - async maxLux(): Promise { - let set_maxLux: number; - if (this.device.curtain?.set_maxLux) { - set_maxLux = this.device.curtain?.set_maxLux; - } else { - set_maxLux = 6001; - } - return set_maxLux; - } - async getLightLevel(lightLevel: any, set_minLux: number, set_maxLux: number, spaceBetweenLevels: number) { switch (lightLevel) { case 1: @@ -410,8 +386,8 @@ export class Hub extends deviceBase { break; case 2: this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel},` + - ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel},` + + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`); break; case 3: this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2; diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 11f54364..945ea9a8 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -94,7 +94,7 @@ export class Humidifier extends deviceBase { validValueRanges: [0, 100], minValue: 0, maxValue: 100, - minStep: this.minStep(device), + minStep: device.humidifier?.set_minStep ?? 1, }) .onGet(() => { return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; @@ -141,34 +141,12 @@ export class Humidifier extends deviceBase { // Start an update interval interval(this.deviceRefreshRate * 1000) .pipe(skipWhile(() => this.humidifierUpdateInProgress)) - .subscribe(() => { - this.refreshStatus(); + .subscribe(async () => { + await this.refreshStatus(); }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { temperature, humidity } = context; - const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; - if (!this.device.humidifier?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Humidifier change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -177,17 +155,15 @@ export class Humidifier extends deviceBase { tap(() => { this.humidifierUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.humidifierUpdateInProgress = false; }); @@ -294,10 +270,8 @@ export class Humidifier extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -358,10 +332,32 @@ export class Humidifier extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { temperature, humidity } = context; + const { CurrentRelativeHumidity } = this.HumidifierDehumidifier; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (temperature, humidity) = Webhook:(${convertUnits(temperature, + context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity; + if (!device.humidifier?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} ` + + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; } } @@ -377,9 +373,8 @@ export class Humidifier extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } interval(5000) .pipe(take(1)) @@ -414,10 +409,8 @@ export class Humidifier extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } @@ -458,10 +451,8 @@ export class Humidifier extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else if ( this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === @@ -513,10 +504,8 @@ export class Humidifier extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushAutoChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushAutoChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushAutoChanges. TargetHumidifierDehumidifierState:` @@ -559,10 +548,8 @@ export class Humidifier extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushActiveChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushActiveChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushActiveChanges. Active: ${this.HumidifierDehumidifier.Active}`); @@ -640,19 +627,18 @@ export class Humidifier extends deviceBase { } } if (this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + - ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); } else { this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + - ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`); this.accessory.context.CurrentHumidifierDehumidifierState = this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState; } if (this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === undefined) { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` - + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`, - ); + + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`); } else { this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, this.HumidifierDehumidifier.TargetHumidifierDehumidifierState); @@ -668,13 +654,13 @@ export class Humidifier extends deviceBase { this.accessory.context.Active = this.HumidifierDehumidifier.Active; } if (this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold === undefined) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + - ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); } else { this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + - ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic` + + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`); this.accessory.context.RelativeHumidityHumidifierThreshold = this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold; } if (!this.device.humidifier?.hide_temperature && !this.BLE) { @@ -705,16 +691,6 @@ export class Humidifier extends deviceBase { } } - minStep(device: device & devicesConfig): number { - let set_minStep: number; - if (device.humidifier?.set_minStep) { - set_minStep = device.humidifier?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - async offlineOff(): Promise { this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} offline: ${this.device.offline}`); if (this.device.offline) { diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 87a79405..3daaccfe 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -152,35 +152,7 @@ export class IOSensor extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { temperature, humidity } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - if (context.scale !== 'CELCIUS' && device.iosensor?.convertUnitTo === undefined) { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: ` - + `${context.scale}, Use the *convertUnitsTo* config under Indoor/Outdoor Sensor settings, if displaying incorrectly in HomeKit.`); - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(scale, temperature, humidity) = ' - + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), ` - + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - if (this.device.iosensor?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (this.device.iosensor?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -265,10 +237,8 @@ export class IOSensor extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -329,10 +299,40 @@ export class IOSensor extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { temperature, humidity } = context; + const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.iosensor?.convertUnitTo === undefined) { + this.warnLog(`${device.deviceType}: ${accessory.displayName} received a non-CELCIUS Webhook scale: ` + + `${context.scale}, Use the *convertUnitsTo* config under Indoor/Outdoor Sensor settings, if displaying incorrectly in HomeKit.`); + } + this.debugLog(`${device.deviceType}: ${accessory.displayName} (scale, temperature, humidity) = Webhook:(${context.scale},` + + ` ${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), current:(${CurrentTemperature}, ` + + `${CurrentRelativeHumidity})`); + if (device.iosensor?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (device.iosensor?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index 4e33b065..e4709146 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -6,7 +6,7 @@ import { interval, skipWhile, Subject } from 'rxjs'; import { debounceTime, take, tap } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; -import type { device, devicesConfig, deviceStatus, serviceData} from '../settings.js'; +import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js'; import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge'; /** @@ -71,10 +71,8 @@ export class StripLight extends deviceBase { }); this.accessory.configureController(this.AdaptiveLightingController); this.accessory.context.adaptiveLighting = true; - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + - ` adaptiveLightingShift: ${this.adaptiveLightingShift}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` + + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`); } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`); @@ -91,7 +89,7 @@ export class StripLight extends deviceBase { this.LightBulb.Service .getCharacteristic(this.hap.Characteristic.Brightness) .setProps({ - minStep: this.minStep(device), + minStep: device.striplight?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -154,75 +152,7 @@ export class StripLight extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { powerState, brightness, color, colorTemperature } = context; - const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(powerState, brightness, color, colorTemperature) = ' + - `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` + - `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); - - // On - this.LightBulb.On = powerState === 'ON' ? true : false; - if (this.accessory.context.Brightness !== this.LightBulb.On) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`); - } - - // Brightness - this.LightBulb.Brightness = brightness; - if (this.accessory.context.Brightness !== this.LightBulb.Brightness) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); - } - - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`); - const [red, green, blue] = color!.split(':'); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); - - const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`, - ); - - // Hue - this.LightBulb.Hue = hue; - if (this.accessory.context.Hue !== this.LightBulb.Hue) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`); - } - - // Saturation - this.LightBulb.Saturation = saturation; - if (this.accessory.context.Saturation !== this.LightBulb.Saturation) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); - } - - // ColorTemperature - this.LightBulb.ColorTemperature = colorTemperature; - if (this.accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { - this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } else { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Bulb change events // We put in a debounce of 1000ms so we don't make duplicate calls @@ -231,17 +161,15 @@ export class StripLight extends deviceBase { tap(() => { this.stripLightUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.stripLightUpdateInProgress = false; }); @@ -269,10 +197,8 @@ export class StripLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${serviceData.blue}`); const [hue, saturation] = rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`); // Hue this.LightBulb.Hue = hue; @@ -317,9 +243,8 @@ export class StripLight extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); // Hue this.LightBulb.Hue = hue; @@ -358,10 +283,8 @@ export class StripLight extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -422,10 +345,79 @@ export class StripLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { powerState, brightness, color, colorTemperature } = context; + const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (powerState, brightness, color, colorTemperature) = Webhook: (${powerState},` + + ` ${brightness}, ${color}, ${colorTemperature}), current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`); + + // On + this.LightBulb.On = powerState === 'ON' ? true : false; + if (accessory.context.Brightness !== this.LightBulb.On) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`); + } + + // Brightness + this.LightBulb.Brightness = brightness; + if (accessory.context.Brightness !== this.LightBulb.Brightness) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`); + } + + this.debugLog(`${device.deviceType}: ${accessory.displayName} color: ${JSON.stringify(color)}`); + const [red, green, blue] = color!.split(':'); + this.debugLog(`${device.deviceType}: ${accessory.displayName} red: ${JSON.stringify(red)}`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} green: ${JSON.stringify(green)}`); + this.debugLog(`${device.deviceType}: ${accessory.displayName} blue: ${JSON.stringify(blue)}`); + + const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); + this.debugLog( + `${device.deviceType}: ${accessory.displayName}` + + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); + + // Hue + this.LightBulb.Hue = hue; + if (accessory.context.Hue !== this.LightBulb.Hue) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`); + } + + // Saturation + this.LightBulb.Saturation = saturation; + if (accessory.context.Saturation !== this.LightBulb.Saturation) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`); + } + + // ColorTemperature + this.LightBulb.ColorTemperature = colorTemperature; + if (accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) { + this.infoLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -448,9 +440,8 @@ export class StripLight extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -499,8 +490,8 @@ export class StripLight extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); // Push Brightness Update @@ -549,18 +540,14 @@ export class StripLight extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + - `Brightness: ${this.LightBulb.Brightness}, ` + - `BrightnessCached: ${this.accessory.context.Brightness}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` + + `Brightness: ${this.LightBulb.Brightness}, ` + + `BrightnessCached: ${this.accessory.context.Brightness}`); } } @@ -595,17 +582,13 @@ export class StripLight extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, HueCached: ` + + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`); } } @@ -646,17 +629,13 @@ export class StripLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.LightBulb.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + + `On: ${this.LightBulb.On}, ` + + `OnCached: ${this.accessory.context.On}`); } // Push Hue & Saturation Update if (this.LightBulb.On) { @@ -704,16 +683,12 @@ export class StripLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, ` + - `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, HueCached: ` + + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`); } } @@ -748,17 +723,13 @@ export class StripLight extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges,` + - `Brightness: ${this.LightBulb.Brightness}, ` + - `BrightnessCached: ${this.accessory.context.Brightness}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges,` + + `Brightness: ${this.LightBulb.Brightness}, ` + + `BrightnessCached: ${this.accessory.context.Brightness}`); } } @@ -920,16 +891,6 @@ export class StripLight extends deviceBase { } } - minStep(device: device & devicesConfig): number { - let set_minStep: number; - if (device.striplight?.set_minStep) { - set_minStep = device.striplight?.set_minStep; - } else { - set_minStep = 1; - } - return set_minStep; - } - async adaptiveLighting(device: device & devicesConfig): Promise { if (device.striplight?.adaptiveLightingShift) { this.adaptiveLightingShift = device.striplight.adaptiveLightingShift; diff --git a/src/device/lock.ts b/src/device/lock.ts index 01b7d8e0..49a75d2c 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -160,25 +160,7 @@ export class Lock extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { lockState } = context; - const { LockCurrentState } = this.LockMechanism; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(lockState) = ' + - `Webhook:(${lockState}), ` + - `current:(${LockCurrentState})`); - this.LockMechanism.LockCurrentState = lockState === 'LOCKED' ? 1 : 0; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Lock change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -187,17 +169,15 @@ export class Lock extends deviceBase { tap(() => { this.lockUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.lockUpdateInProgress = false; }); @@ -302,10 +282,8 @@ export class Lock extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -366,10 +344,28 @@ export class Lock extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { lockState } = context; + const { LockCurrentState } = this.LockMechanism; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (lockState) = Webhook:(${lockState}), current:(${LockCurrentState})`); + this.LockMechanism.LockCurrentState = lockState === 'LOCKED' ? 1 : 0; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -382,15 +378,14 @@ export class Lock extends deviceBase { async pushChanges(): Promise { if (!this.device.enableCloudService && this.OpenAPI) { this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`); - /*} else if (this.BLE) { - await this.BLEpushChanges();*/ + } else if (this.BLE) { + await this.BLEpushChanges(); } else if (this.OpenAPI && this.platform.config.credentials?.token) { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -404,10 +399,8 @@ export class Lock extends deviceBase { async BLEpushChanges(): Promise { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`); if (this.LockMechanism.LockTargetState !== this.accessory.context.LockTargetState) { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges LockTargetState: ${this.LockMechanism.LockTargetState}` + - ` LockTargetStateCached: ${this.accessory.context.LockTargetState}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges LockTargetState: ${this.LockMechanism.LockTargetState}` + + ` LockTargetStateCached: ${this.accessory.context.LockTargetState}`); const switchbot = await this.platform.connectBLE(); // Convert to BLE Address this.device.bleMac = this.device @@ -428,18 +421,14 @@ export class Lock extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + - `LockTargetState: ${this.LockMechanism.LockTargetState}, ` + - `LockTargetStateCached: ${this.accessory.context.LockTargetState}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + + `LockTargetState: ${this.LockMechanism.LockTargetState}, ` + + `LockTargetStateCached: ${this.accessory.context.LockTargetState}`); } } @@ -482,17 +471,12 @@ export class Lock extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `LockTargetState: ${this.LockMechanism.LockTargetState}, ` + - `LockTargetStateCached: ${this.accessory.context.LockTargetState}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges, LockTargetState: ` + + `${this.LockMechanism.LockTargetState}, LockTargetStateCached: ${this.accessory.context.LockTargetState}`); } } diff --git a/src/device/meter.ts b/src/device/meter.ts index 8e8718ea..15c4e485 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -9,8 +9,8 @@ import { convertUnits } from '../utils.js'; import { Subject, interval, skipWhile } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; -import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; export class Meter extends deviceBase { // Services @@ -145,35 +145,7 @@ export class Meter extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { temperature, humidity } = context; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - if (context.scale !== 'CELCIUS' && device.meter?.convertUnitTo === undefined) { - this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook scale: ` - + `${context.scale}, instead of CELCIUS. Use the *convertUnitsTo* config under Meter settings, if displaying incorrectly in HomeKit.`); - } - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(scale, temperature, humidity) = ' - + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.meter?.convertUnitTo)}, ${humidity}), ` - + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - if (this.device.meter?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (this.device.meter?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.meter?.convertUnitTo); - } - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -258,10 +230,8 @@ export class Meter extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -322,10 +292,41 @@ export class Meter extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { temperature, humidity } = context; + const { CurrentTemperature } = this.TemperatureSensor ?? { CurrentTemperature: undefined }; + const { CurrentRelativeHumidity } = this.HumiditySensor ?? { CurrentRelativeHumidity: undefined }; + if (context.scale !== 'CELCIUS' && device.meter?.convertUnitTo === undefined) { + this.warnLog(`${device.deviceType}: ${accessory.displayName} received Webhook scale: ` + + `${context.scale}, instead of CELCIUS. Use the *convertUnitsTo* config under Meter settings, if displaying incorrectly in HomeKit.`); + } + this.debugLog(`${device.deviceType}: ${accessory.displayName} ` + + '(scale, temperature, humidity) = ' + + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.meter?.convertUnitTo)}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + if (!device.meter?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (!device.meter?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.meter?.convertUnitTo); + } + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index fc0dc859..b75063ba 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -8,8 +8,8 @@ import { deviceBase } from './device.js'; import { Subject, interval, skipWhile } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; -import type { CharacteristicValue, PlatformAccessory, Service} from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -149,33 +149,7 @@ export class MeterPlus extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - if (context.scale === 'CELSIUS') { - const { temperature, humidity } = context; - const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined }; - const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined }; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(temperature, humidity) = ' + - `Webhook:(${temperature}, ${humidity}), ` + - `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); - if (this.device.meter?.hide_humidity) { - this.HumiditySensor!.CurrentRelativeHumidity = humidity; - } - if (this.device.meter?.hide_temperature) { - this.TemperatureSensor!.CurrentTemperature = temperature; - } - this.updateHomeKitCharacteristics(); - } - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -260,10 +234,8 @@ export class MeterPlus extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -324,10 +296,37 @@ export class MeterPlus extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + if (context.scale === 'CELSIUS') { + const { temperature, humidity } = context; + const { CurrentRelativeHumidity } = this.HumiditySensor ?? { CurrentRelativeHumidity: undefined }; + const { CurrentTemperature } = this.TemperatureSensor ?? { CurrentTemperature: undefined }; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (temperature, humidity) = Webhook:(${temperature}, ${humidity}), ` + + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`); + if (!device.meter?.hide_humidity) { + this.HumiditySensor!.CurrentRelativeHumidity = humidity; + } + if (!device.meter?.hide_temperature) { + this.TemperatureSensor!.CurrentTemperature = temperature; + } + this.updateHomeKitCharacteristics(); + } + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } diff --git a/src/device/motion.ts b/src/device/motion.ts index 37788aa5..d59f83f5 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -137,25 +137,7 @@ export class Motion extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { detectionState } = context; - const { MotionDetected } = this.MotionSensor; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(detectionState) = ' + - `Webhook:(${detectionState}), ` + - `current:(${MotionDetected})`); - this.MotionSensor.MotionDetected = detectionState === 'DETECTED' ? true : false; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -168,8 +150,8 @@ export class Motion extends deviceBase { } // Light Level if (!this.device.motion?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.motion?.set_minLux ?? 1; + const set_maxLux = this.device.motion?.set_maxLux ?? 6001; switch (serviceData.lightLevel) { case 'dark': case 1: @@ -179,8 +161,7 @@ export class Motion extends deviceBase { this.LightSensor!.CurrentAmbientLightLevel = set_maxLux; } this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},` - + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); if (this.LightSensor!.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) { this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName}` + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); @@ -196,10 +177,8 @@ export class Motion extends deviceBase { } else { this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` - + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},` + + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`); } async openAPIparseStatus(deviceStatus: deviceStatus): Promise { @@ -209,8 +188,8 @@ export class Motion extends deviceBase { this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor.MotionDetected}`); // Light Level if (!this.device.motion?.hide_lightsensor) { - const set_minLux = await this.minLux(); - const set_maxLux = await this.maxLux(); + const set_minLux = this.device.motion?.set_minLux ?? 1; + const set_maxLux = this.device.motion?.set_maxLux ?? 6001; switch (deviceStatus.body.brightness) { case 'dim': this.LightSensor!.CurrentAmbientLightLevel = set_minLux; @@ -264,10 +243,8 @@ export class Motion extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -328,13 +305,31 @@ export class Motion extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { detectionState } = context; + const { MotionDetected } = this.MotionSensor; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (detectionState) = Webhook: (${detectionState}),` + + ` current: (${MotionDetected})`); + this.MotionSensor.MotionDetected = detectionState === 'DETECTED' ? true : false; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); + } + } /** * Updates the status for each of the HomeKit Characteristics @@ -370,10 +365,8 @@ export class Motion extends deviceBase { } else { this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel; this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel); - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`); } } } @@ -387,26 +380,6 @@ export class Motion extends deviceBase { } } - async minLux(): Promise { - let set_minLux: number; - if (this.device.motion?.set_minLux) { - set_minLux = this.device.motion!.set_minLux!; - } else { - set_minLux = 1; - } - return set_minLux; - } - - async maxLux(): Promise { - let set_maxLux: number; - if (this.device.motion?.set_maxLux) { - set_maxLux = this.device.motion!.set_maxLux!; - } else { - set_maxLux = 6001; - } - return set_maxLux; - } - async offlineOff(): Promise { if (this.device.offline) { this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); diff --git a/src/device/plug.ts b/src/device/plug.ts index fa0adf09..f7dca7e3 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -9,7 +9,7 @@ import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs'; import type { SwitchBotPlatform } from '../platform.js'; import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; export class Plug extends deviceBase { // Services private Outlet: { @@ -64,25 +64,7 @@ export class Plug extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { powerState } = context; - const { On } = this.Outlet; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(powerState) = ' + - `Webhook:(${powerState}), ` + - `current:(${On})`); - this.Outlet.On = powerState === 'ON' ? true : false; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Plug change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -91,17 +73,15 @@ export class Plug extends deviceBase { tap(() => { this.plugUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { await this.pushChanges(); } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.plugUpdateInProgress = false; }); @@ -159,10 +139,8 @@ export class Plug extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -223,10 +201,28 @@ export class Plug extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { powerState } = context; + const { On } = this.Outlet; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (powerState) = Webhook: (${powerState}), current:(${On})`); + this.Outlet.On = powerState === 'ON' ? true : false; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -249,9 +245,8 @@ export class Plug extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -300,10 +295,8 @@ export class Plug extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 3d0e043e..972e1f35 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -121,29 +121,7 @@ export class RobotVacuumCleaner extends deviceBase { }); //regisiter webhook event handler - if (device.webhook) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { onlineStatus, battery, workingStatus } = context; - const { On } = this.LightBulb; - const { BatteryLevel, ChargingState } = this.Battery; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(onlineStatus, battery, workingStatus) = ' + - `Webhook:(${onlineStatus}, ${battery}, ${workingStatus}), ` + - `current:(${On}, ${BatteryLevel}, ${ChargingState})`); - this.LightBulb.On = onlineStatus === 'online' ? true : false; - this.Battery.ChargingState = workingStatus === 'Charging' - ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; - this.Battery.BatteryLevel = battery; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); // Watch for Plug change events // We put in a debounce of 100ms so we don't make duplicate calls @@ -152,22 +130,20 @@ export class RobotVacuumCleaner extends deviceBase { tap(() => { this.robotVacuumCleanerUpdateInProgress = true; }), - debounceTime(this.platform.config.options!.pushRate! * 1000), + debounceTime(this.devicePushRate * 1000), ) .subscribe(async () => { try { - if (this.LightBulb.On !== this.accessory.context.On) { + if (this.LightBulb.On !== accessory.context.On) { await this.pushChanges(); } - if (this.LightBulb.On && this.LightBulb.Brightness !== this.accessory.context.Brightness) { + if (this.LightBulb.On && this.LightBulb.Brightness !== accessory.context.Brightness) { await this.openAPIpushBrightnessChanges(); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` + - ` Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); } this.robotVacuumCleanerUpdateInProgress = false; }); @@ -253,10 +229,8 @@ export class RobotVacuumCleaner extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -317,10 +291,33 @@ export class RobotVacuumCleaner extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { onlineStatus, battery, workingStatus } = context; + const { On } = this.LightBulb; + const { BatteryLevel, ChargingState } = this.Battery; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (onlineStatus, battery, workingStatus) = ` + + `Webhook: (${onlineStatus}, ${battery}, ${workingStatus}), current: (${On}, ${BatteryLevel}, ${ChargingState})`); + this.LightBulb.On = onlineStatus === 'online' ? true : false; + this.Battery.ChargingState = workingStatus === 'Charging' + ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING; + this.Battery.BatteryLevel = battery; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } @@ -342,9 +339,8 @@ export class RobotVacuumCleaner extends deviceBase { await this.openAPIpushChanges(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, pushChanges will not happen.`); } // Refresh the status from the API interval(15000) @@ -393,10 +389,8 @@ export class RobotVacuumCleaner extends deviceBase { }) .catch(async (e: any) => { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); await this.BLEPushConnection(); }); } else { @@ -425,24 +419,19 @@ export class RobotVacuumCleaner extends deviceBase { this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`); this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); + + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`); } else { this.statusCode(statusCode); this.statusCode(deviceStatus.statusCode); } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.debugLog( - `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + - `On: ${this.LightBulb.On}, ` + - `OnCached: ${this.accessory.context.On}`, - ); + this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges, On: ${this.LightBulb.On}, ` + + `OnCached: ${this.accessory.context.On}`); } } @@ -471,10 +460,8 @@ export class RobotVacuumCleaner extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index ce8188e3..63220e17 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -9,7 +9,7 @@ import { skipWhile } from 'rxjs/operators'; import type { SwitchBotPlatform } from '../platform.js'; import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; -import type { device, devicesConfig, serviceData, deviceStatus} from '../settings.js'; +import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js'; /** * Platform Accessory @@ -43,8 +43,6 @@ export class WaterDetector extends deviceBase { device: device & devicesConfig, ) { super(platform, accessory, device); - // default placeholders - this.setupHistoryService(device); // this is subject we use to track when we need to POST changes to the SwitchBot API this.doWaterDetectorUpdate = new Subject(); @@ -82,6 +80,8 @@ export class WaterDetector extends deviceBase { this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leak Sensor Service`); this.LeakSensor.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service; accessory.removeService(this.LeakSensor.Service); + } else { + this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Leak Sensor Service Not Found`); } } else { accessory.context.LeakSensor = accessory.context.LeakSensor ?? {}; @@ -117,27 +117,7 @@ export class WaterDetector extends deviceBase { }); //regisiter webhook event handler - if (device.webhook && !this.device.waterdetector?.hide_leak) { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`); - this.platform.webhookEventHandler[this.device.deviceId] = async (context) => { - try { - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`); - const { detectionState, battery } = context; - const { LeakDetected } = this.LeakSensor!; - const { BatteryLevel } = this.Battery!; - this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` + - '(detectionState, battery) = ' + - `Webhook:(${detectionState}, ${battery}), ` + - `current:(${LeakDetected}, ${BatteryLevel})`); - this.LeakSensor!.LeakDetected = detectionState; - this.Battery!.BatteryLevel = battery; - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} ` - + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); - } - }; - } + this.registerWebhook(accessory, device); } async BLEparseStatus(serviceData: serviceData): Promise { @@ -206,10 +186,8 @@ export class WaterDetector extends deviceBase { await this.openAPIRefreshStatus(); } else { await this.offlineOff(); - this.debugWarnLog( - `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + - ` ${this.device.connectionType}, refreshStatus will not happen.`, - ); + this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + + ` ${this.device.connectionType}, refreshStatus will not happen.`); } } @@ -270,10 +248,33 @@ export class WaterDetector extends deviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); + } + } + + async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) { + if (device.webhook) { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`); + this.platform.webhookEventHandler[device.deviceId] = async (context) => { + try { + this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`); + const { detectionState, battery } = context; + const { LeakDetected } = this.LeakSensor ? this.LeakSensor : { LeakDetected: undefined }; + const { BatteryLevel } = this.Battery ? this.Battery : { BatteryLevel: undefined }; + this.debugLog(`${device.deviceType}: ${accessory.displayName} (detectionState, battery) = Webhook: (${detectionState}, ${battery}), ` + + `current: (${LeakDetected}, ${BatteryLevel})`); + if (!device.waterdetector?.hide_leak) { + this.LeakSensor!.LeakDetected = detectionState; + } + this.Battery.BatteryLevel = battery; + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`); + } + }; + } else { + this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`); } } diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index eb95f160..21da7df8 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -151,7 +151,7 @@ export class AirConditioner extends irdeviceBase { this.meter = this.platform.accessories.find((accessory) => accessory.UUID === meterUuid); accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {}; this.HumiditySensor = { - Name: accessory.context.HumiditySensor ?? this.meter!.displayName, + Name: accessory.context.HumiditySensor ?? this.meter!.displayName, Service: this.meter!.getService(this.hap.Service.HumiditySensor) ?? this.meter!.addService(this.hap.Service.HumiditySensor) as Service, CurrentRelativeHumidity: this.meter!.context.CurrentRelativeHumidity || 0, }; @@ -182,10 +182,8 @@ export class AirConditioner extends irdeviceBase { * AirConditioner: "command" "highSpeed" "default" = fan speed to high */ async pushAirConditionerOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges Active: ${this.HeaterCooler.Active},` + - ` disablePushOn: ${this.disablePushOn}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOnChanges Active: ${this.HeaterCooler.Active},` + + ` disablePushOn: ${this.disablePushOn}`); if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); @@ -199,10 +197,8 @@ export class AirConditioner extends irdeviceBase { } async pushAirConditionerOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges Active: ${this.HeaterCooler.Active},` + - ` disablePushOff: ${this.disablePushOff}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerOffChanges Active: ${this.HeaterCooler.Active},` + + ` disablePushOff: ${this.disablePushOff}`); if (this.HeaterCooler.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); @@ -216,10 +212,8 @@ export class AirConditioner extends irdeviceBase { } async pushAirConditionerStatusChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerStatusChanges Active: ${this.HeaterCooler.Active},` + - ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerStatusChanges Active: ${this.HeaterCooler.Active},` + + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`); if (!this.Busy) { this.Busy = true; this.HeaterCooler.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; @@ -231,10 +225,8 @@ export class AirConditioner extends irdeviceBase { } async pushAirConditionerDetailsChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerDetailsChanges Active: ${this.HeaterCooler.Active},` + - ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirConditionerDetailsChanges Active: ${this.HeaterCooler.Active},` + + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`); //await this.deviceContext(); if (this.CurrentMode === undefined) { this.CurrentMode = 1; @@ -250,10 +242,8 @@ export class AirConditioner extends irdeviceBase { if (this.CurrentMode === 1) { // Remove or make configurable? this.HeaterCooler.ThresholdTemperature = 25; - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} CurrentMode: ${this.CurrentMode},` + - ` ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} CurrentMode: ${this.CurrentMode},` + + ` ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature}`); } const parameter = `${this.HeaterCooler.ThresholdTemperature},${this.CurrentMode},${this.CurrentFanSpeed},${this.state}`; @@ -382,20 +372,14 @@ export class AirConditioner extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, disablePushDetails: ${this.disablePushDetail}`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, disablePushDetails: ${this.disablePushDetail}`); this.updateHomeKitCharacteristics(); } } @@ -403,10 +387,8 @@ export class AirConditioner extends irdeviceBase { async CurrentTemperatureGet(): Promise { if (this.meter?.context?.CurrentTemperature) { this.accessory.context.CurrentTemperature = this.meter.context.CurrentTemperature; - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} ` - + `Using CurrentTemperature from ${this.meter.context.deviceType} (${this.meter.context.deviceID})`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + + `Using CurrentTemperature from ${this.meter.context.deviceType} (${this.meter.context.deviceID})`); } this.HeaterCooler.CurrentTemperature = this.accessory.context.CurrentTemperature || 24; @@ -417,10 +399,8 @@ export class AirConditioner extends irdeviceBase { async CurrentRelativeHumidityGet(): Promise { if (this.meter?.context?.CurrentRelativeHumidity) { this.accessory.context.CurrentRelativeHumidity = this.meter.context.CurrentRelativeHumidity; - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} ` - + `Using CurrentRelativeHumidity from ${this.meter.context.deviceType} (${this.meter.context.deviceID})`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} ` + + `Using CurrentRelativeHumidity from ${this.meter.context.deviceType} (${this.meter.context.deviceID})`); } this.HumiditySensor!.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity || 0; @@ -446,8 +426,8 @@ export class AirConditioner extends irdeviceBase { this.CurrentFanSpeed = Number(value) + 1; } this.HeaterCooler.RotationSpeed = value; - this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + - `Set RotationSpeed: ${this.HeaterCooler.RotationSpeed}, CurrentFanSpeed: ${this.CurrentFanSpeed}`); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + `Set RotationSpeed: ${this.HeaterCooler.RotationSpeed}, CurrentFanSpeed: ${this.CurrentFanSpeed}`); this.pushAirConditionerStatusChanges(); } @@ -484,10 +464,8 @@ export class AirConditioner extends irdeviceBase { } else if (value === this.hap.Characteristic.TargetHeaterCoolerState.COOL) { this.TargetHeaterCoolerStateCOOL(); } else { - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} Set TargetHeaterCoolerState: ${this.HeaterCooler.TargetHeaterCoolerState},` + - ` hide_automode: ${this.hide_automode} `, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Set TargetHeaterCoolerState: ` + + `${this.HeaterCooler.TargetHeaterCoolerState}, hide_automode: ${this.hide_automode} `); } this.pushAirConditionerStatusChanges(); } @@ -518,10 +496,8 @@ export class AirConditioner extends irdeviceBase { async CurrentHeaterCoolerStateGet(): Promise { await this.UpdateCurrentHeaterCoolerState(); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Get (${this.getTargetHeaterCoolerStateName()}) CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Get (${this.getTargetHeaterCoolerStateName()}) CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); return this.HeaterCooler.CurrentHeaterCoolerState; } @@ -583,10 +559,8 @@ export class AirConditioner extends irdeviceBase { async ThresholdTemperatureSet(value: CharacteristicValue): Promise { this.HeaterCooler.ThresholdTemperature = value; - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} Set ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature},` + - ` ThresholdTemperatureCached: ${this.accessory.context.ThresholdTemperature}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Set ThresholdTemperature: ${this.HeaterCooler.ThresholdTemperature},` + + ` ThresholdTemperatureCached: ${this.accessory.context.ThresholdTemperature}`); this.pushAirConditionerStatusChanges(); } @@ -626,10 +600,8 @@ export class AirConditioner extends irdeviceBase { this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity; this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor!.CurrentRelativeHumidity); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` - + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`); } } // TargetHeaterCoolerState @@ -648,10 +620,8 @@ export class AirConditioner extends irdeviceBase { } else { this.accessory.context.CurrentHeaterCoolerState = this.HeaterCooler.CurrentHeaterCoolerState; this.HeaterCooler.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.HeaterCooler.CurrentHeaterCoolerState); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentHeaterCoolerState: ${this.HeaterCooler.CurrentHeaterCoolerState}`); } // ThresholdTemperature if (this.HeaterCooler.ThresholdTemperature === undefined) { diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 4347d3b4..cb61640d 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -154,10 +154,8 @@ export class AirPurifier extends irdeviceBase { * AirPurifier: "command" "highSpeed" "default" = fan speed to high */ async pushAirPurifierOnChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOnChanges Active: ${this.AirPurifier.Active},` + - ` disablePushOn: ${this.disablePushOn}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOnChanges Active: ${this.AirPurifier.Active},` + + ` disablePushOn: ${this.disablePushOn}`); if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOn(); @@ -171,10 +169,8 @@ export class AirPurifier extends irdeviceBase { } async pushAirPurifierOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOffChanges Active: ${this.AirPurifier.Active},` + - ` disablePushOff: ${this.disablePushOff}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierOffChanges Active: ${this.AirPurifier.Active},` + + ` disablePushOff: ${this.disablePushOff}`); if (this.AirPurifier.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); @@ -188,10 +184,8 @@ export class AirPurifier extends irdeviceBase { } async pushAirPurifierStatusChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierStatusChanges Active: ${this.AirPurifier.Active},` + - ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierStatusChanges Active: ${this.AirPurifier.Active},` + + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`); if (!this.Busy) { this.Busy = true; this.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE; @@ -203,13 +197,11 @@ export class AirPurifier extends irdeviceBase { } async pushAirPurifierDetailsChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierDetailsChanges Active: ${this.AirPurifier.Active},` + - ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`, - ); - this.CurrentAPTemp = this.TemperatureSensor!.CurrentTemperature || 24; - this.CurrentAPMode = this.CurrentMode || 1; - this.CurrentAPFanSpeed = this.CurrentFanSpeed || 1; + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushAirPurifierDetailsChanges Active: ${this.AirPurifier.Active},` + + ` disablePushOff: ${this.disablePushOff}, disablePushOn: ${this.disablePushOn}`); + this.CurrentAPTemp = this.TemperatureSensor!.CurrentTemperature ?? 24; + this.CurrentAPMode = this.CurrentMode ?? 1; + this.CurrentAPFanSpeed = this.CurrentFanSpeed ?? 1; this.APActive = this.AirPurifier.Active === 1 ? 'on' : 'off'; const parameter = `${this.CurrentAPTemp},${this.CurrentAPMode},${this.CurrentAPFanSpeed},${this.APActive}`; const bodyChange = JSON.stringify({ @@ -255,16 +247,12 @@ export class AirPurifier extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } @@ -292,10 +280,8 @@ export class AirPurifier extends irdeviceBase { } else { this.accessory.context.CurrentHeaterCoolerState = this.CurrentHeaterCoolerState; this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` updateCharacteristic CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic` + + ` CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); } // CurrentTemperature if (this.TemperatureSensor.CurrentTemperature === undefined) { diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 3448e599..b1035c02 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -85,9 +85,8 @@ export class Camera extends irdeviceBase { } async pushOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.Switch.On},` + ` disablePushOff: ${this.disablePushOff}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushOffChanges On: ${this.Switch.On},` + + ` disablePushOff: ${this.disablePushOff}`); if (!this.Switch.On && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); @@ -126,16 +125,12 @@ export class Camera extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index bd99887a..95238c69 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -58,9 +58,9 @@ export class IRFan extends irdeviceBase { this.Fan.Service .getCharacteristic(this.hap.Characteristic.RotationSpeed) .setProps({ - minStep: Number(this.minStep(device)), - minValue: Number(this.minValue(device)), - maxValue: Number(this.maxValue(device)), + minStep: device.irfan?.set_minStep ?? 1, + minValue: device.irfan?.set_min ?? 1, + maxValue: device.irfan?.set_max ?? 100, }) .onGet(() => { return this.Fan.RotationSpeed; @@ -71,10 +71,8 @@ export class IRFan extends irdeviceBase { this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Rotation Speed Characteristic was removed.`); } else { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed Characteristic was not removed/added, ` + - `Clear Cache on ${this.accessory.displayName} to remove Chracteristic`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} RotationSpeed Characteristic was not removed/added, ` + + `Clear Cache on ${this.accessory.displayName} to remove Chracteristic`); } if (device.irfan?.swing_mode) { @@ -90,43 +88,11 @@ export class IRFan extends irdeviceBase { this.Fan.Service.removeCharacteristic(characteristic); this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was removed.`); } else { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was not removed/added, ` + - `Clear Cache on ${this.accessory.displayName} To Remove Chracteristic`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Swing Mode Characteristic was not removed/added, ` + + `Clear Cache on ${this.accessory.displayName} To Remove Chracteristic`); } } - async minStep(device: irdevice & irDevicesConfig): Promise { - let minStep: number; - if (device.irfan?.set_minStep) { - minStep = device.irfan?.set_minStep; - } else { - minStep = 1; - } - return minStep; - } - - async minValue(device: irdevice & irDevicesConfig): Promise { - let minValue: number; - if (device.irfan?.set_min) { - minValue = device.irfan?.set_min; - } else { - minValue = 1; - } - return minValue; - } - - async maxValue(device: irdevice & irDevicesConfig): Promise { - let maxValue: number; - if (device.irfan?.set_max) { - maxValue = device.irfan?.set_max; - } else { - maxValue = 100; - } - return maxValue; - } - async SwingModeSet(value: CharacteristicValue): Promise { this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} SwingMode: ${value}`); if (value > this.Fan.SwingMode) { @@ -192,10 +158,8 @@ export class IRFan extends irdeviceBase { } async pushFanOffChanges(): Promise { - this.debugLog( - `${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges Active: ${this.Fan.Active},` + - ` disablePushOff: ${this.disablePushOff}`, - ); + this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushLightOffChanges Active: ${this.Fan.Active},` + + ` disablePushOff: ${this.disablePushOff}`); if (this.Fan.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType(); const command: string = await this.commandOff(); @@ -261,16 +225,12 @@ export class IRFan extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index d39ca885..6508986f 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -38,13 +38,12 @@ export abstract class irdeviceBase { accessory .getService(this.hap.Service.AccessoryInformation)! .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot') - .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName) - .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name) - .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model) - .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId) - .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.firmware) - .getCharacteristic(this.hap.Characteristic.FirmwareRevision) - .updateValue(accessory.context.firmware); + .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760') + .setCharacteristic(this.hap.Characteristic.Name, accessory.context.name ?? accessory.displayName) + .setCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.context.name ?? accessory.displayName) + .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model ?? 'Unknown') + .setCharacteristic(this.hap.Characteristic.ProductData, accessory.context.deviceId) + .setCharacteristic(this.hap.Characteristic.SerialNumber, accessory.context.deviceId); } async getDeviceLogSettings(device: irdevice & irDevicesConfig): Promise { @@ -151,6 +150,43 @@ export abstract class irdeviceBase { } else { accessory.context.firmware = 'Unknown'; } + + // Firmware Version + let deviceFirmwareVersion: string; + if (device.firmware) { + deviceFirmwareVersion = device.firmware; + this.debugSuccessLog(`${device.remoteType}: ${accessory.displayName} 1 FirmwareRevision: ${device.firmware}`); + } else if (accessory.context.deviceVersion) { + deviceFirmwareVersion = accessory.context.deviceVersion; + this.debugSuccessLog(`${device.remoteType}: ${accessory.displayName} 2 FirmwareRevision: ${accessory.context.deviceVersion}`); + } else { + deviceFirmwareVersion = this.platform.version ?? '0.0.0'; + if (this.platform.version) { + this.debugSuccessLog(`${device.remoteType}: ${accessory.displayName} 3 FirmwareRevision: ${this.platform.version}`); + } else { + this.debugSuccessLog(`${device.remoteType}: ${accessory.displayName} 4 FirmwareRevision: ${deviceFirmwareVersion}`); + } + } + const version = deviceFirmwareVersion.toString(); + this.debugLog(`${this.device.remoteType}: ${accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`); + let deviceVersion: string; + if (version?.includes('.') === false) { + const replace = version?.replace(/^V|-.*$/g, ''); + const match = replace?.match(/.{1,1}/g); + const validVersion = match?.join('.'); + deviceVersion = validVersion ?? '0.0.0'; + } else { + deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0'; + } + accessory + .getService(this.hap.Service.AccessoryInformation)! + .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.SoftwareRevision, deviceVersion) + .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion) + .getCharacteristic(this.hap.Characteristic.FirmwareRevision) + .updateValue(deviceVersion); + accessory.context.deviceVersion = deviceVersion; + this.debugSuccessLog(`${device.remoteType}: ${accessory.displayName} deviceVersion: ${accessory.context.deviceVersion}`); } async disablePushOnChanges(device: irdevice & irDevicesConfig): Promise { @@ -224,12 +260,12 @@ export abstract class irdeviceBase { this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`); break; case 171: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + - `Hub: ${this.device.hubDeviceId}`); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` + + `Hub: ${this.device.hubDeviceId}`); break; case 190: - this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + - ` server, Or command format is invalid, statusCode: ${statusCode}`); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with` + + ` server, Or command format is invalid, statusCode: ${statusCode}`); break; case 100: this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`); diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index 57d543a7..5009f135 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -388,16 +388,12 @@ export class TV extends irdeviceBase { } } catch (e: any) { this.apiError(e); - this.errorLog( - `${this.device.remoteType}: ${this.accessory.displayName} failed pushTVChanges with ${this.device.connectionType}` + - ` Connection, Error Message: ${JSON.stringify(e.message)}`, - ); + this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushTVChanges with ${this.device.connectionType}` + + ` Connection, Error Message: ${JSON.stringify(e.message)}`); } } else { - this.warnLog( - `${this.device.remoteType}: ${this.accessory.displayName}` + - ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`, - ); + this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`); } } diff --git a/src/platform.ts b/src/platform.ts index 7d274709..0cae14eb 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -81,9 +81,11 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { config: SwitchBotPlatformConfig, api: API, ) { + // initialize this.accessories = []; this.api = api; this.log = log; + // only load if configured if (!config) { return; @@ -96,10 +98,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { credentials: config.credentials as object, options: config.options as object, }; - this.platformLogging = this.config.options?.logging ?? 'standard'; - this.platformConfigOptions(); - this.platformLogs(); + + // Plugin Configuration + this.getPlatformConfigSettings(); + this.getPlatformLogSettings(); this.getVersion(); + + // Finish initializing the platform this.debugLog(`Finished initializing platform: ${config.name}`); // verify the config @@ -627,8 +632,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } catch (e: any) { retryCount++; this.debugErrorLog( - `Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug', - ); + `Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug'); this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`); } } @@ -746,7 +750,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { break; default: this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`, - + `Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}` ); + + `Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`); } } @@ -1401,8 +1405,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (device.group && !device.curtain?.disable_group) { this.debugLog( 'Your Curtains are grouped, ' + - `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`, - ); + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`); } else { if (device.master) { this.warnLog(`Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`); @@ -1474,8 +1477,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (device.group && !device.curtain?.disable_group) { this.debugLog( 'Your Curtains are grouped, ' + - `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`, - ); + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`); } else { if (device.master) { this.warnLog(`Main Curtain: ${device.deviceName}, DeviceID: ${device.deviceId}`); @@ -2468,8 +2470,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { switch (device.deviceType) { case 'Curtain': case 'Curtain3': - this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds}, master: ` + - `${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`); + this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds}, master: ` + + `${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`); break; default: this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, blindTiltDevicesIds: ${device.blindTiltDevicesIds}, master:` @@ -2480,41 +2482,32 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (device.master && device.group) { // OpenAPI: Master Curtains/Blind Tilt in Group registerCurtain = true; - this.debugLog( - `deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group}` + - ` connectionType; ${device.connectionType}`, - ); + this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group}` + + ` connectionType; ${device.connectionType}`); this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`); } else if (!device.master && device.curtain?.disable_group) { //!device.group && device.connectionType === 'BLE' // OpenAPI: Non-Master Curtains/Blind Tilts that has Disable Grouping Checked registerCurtain = true; - this.debugLog( - `deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ` + - `${device.curtain?.disable_group}, connectionType; ${device.connectionType}`, - ); + this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ` + + `${device.curtain?.disable_group}, connectionType; ${device.connectionType}`); this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`); } else if (device.master && !device.group) { // OpenAPI: Master Curtains/Blind Tilts not in Group registerCurtain = true; - this.debugLog( - `deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group}` + - ` connectionType; ${device.connectionType}`, - ); + this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group}` + + ` connectionType; ${device.connectionType}`); this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`); } else if (device.connectionType === 'BLE') { // BLE: Curtains/Blind Tilt registerCurtain = true; - this.debugLog( - `deviceName: ${device.deviceName} [${device.deviceType} Config] connectionType: ${device.connectionType}, ` + ` group: ${device.group}`, - ); + this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] connectionType: ${device.connectionType}, ` + + ` group: ${device.group}`); this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`); } else { registerCurtain = false; - this.debugErrorLog( - `deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${device.curtain?.disable_group},` + - ` device.master: ${device.master}, device.group: ${device.group}`, - ); + this.debugErrorLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${device.curtain?.disable_group},` + + ` device.master: ${device.master}, device.group: ${device.group}`); this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}, device.connectionType: ${device.connectionType}`); } return registerCurtain; @@ -2605,10 +2598,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.debugErrorLog(`Device: ${device.deviceName} hide_device: ${device.hide_device}, will not display in HomeKit`); } else { registerDevice = false; - this.debugErrorLog( - `Device: ${device.deviceName} connectionType: ${device.connectionType}, hide_device: ` + - `${device.hide_device}, will not display in HomeKit`, - ); + this.debugErrorLog(`Device: ${device.deviceName} connectionType: ${device.connectionType}, hide_device: ` + + `${device.hide_device}, will not display in HomeKit`); } return registerDevice; } @@ -2651,16 +2642,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { async statusCode(statusCode: number): Promise { switch (statusCode) { case 151: - this.errorLog( - `Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here: ` + - 'https://tinyurl.com/SwitchBotFeatureRequest', - ); + this.errorLog(`Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here: ` + + 'https://tinyurl.com/SwitchBotFeatureRequest'); break; case 152: this.errorLog(`Device not found, statusCode: ${statusCode}`); break; case 160: - this.errorLog(`Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`); + this.errorLog(`Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`); break; case 161: this.errorLog(`Device is offline, statusCode: ${statusCode}`); @@ -2765,7 +2754,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.version = json.version; } - async platformConfigOptions() { + async getPlatformConfigSettings() { const platformConfig: SwitchBotPlatformConfig['options'] = {}; if (this.config.options) { if (this.config.options.logging) { @@ -2774,6 +2763,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (this.config.options.refreshRate) { platformConfig.refreshRate = this.config.options.refreshRate; } + if (this.config.options.updateRate) { + platformConfig.updateRate = this.config.options.updateRate; + } if (this.config.options.pushRate) { platformConfig.pushRate = this.config.options.pushRate; } @@ -2800,10 +2792,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - async platformLogs() { + async getPlatformLogSettings() { this.debugMode = process.argv.includes('-D') || process.argv.includes('--debug'); if (this.config.options?.logging === 'debug' || this.config.options?.logging === 'standard' || this.config.options?.logging === 'none') { - this.platformLogging = this.config.options!.logging; + this.platformLogging = this.config.options.logging; this.debugWarnLog(`Using Config Logging: ${this.platformLogging}`); } else if (this.debugMode) { this.platformLogging = 'debugMode'; @@ -2818,15 +2810,23 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { * If device level logging is turned on, log to log.warn * Otherwise send debug logs to log.debug */ + infoLog(...log: any[]): void { + if (this.enablingPlatformLogging()) { + this.log.info(String(...log)); + } + } + successLog(...log: any[]): void { if (this.enablingPlatformLogging()) { this.log.success(String(...log)); } } - infoLog(...log: any[]): void { + debugSuccessLog(...log: any[]): void { if (this.enablingPlatformLogging()) { - this.log.info(String(...log)); + if (this.platformLogging?.includes('debug')) { + this.log.success('[DEBUG]', String(...log)); + } } } diff --git a/src/settings.ts b/src/settings.ts index acd97c68..4226857b 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -54,6 +54,7 @@ export type credentials = { export type options = { refreshRate?: number; + updateRate?: number; pushRate?: number; maxRetries?: number; delayBetweenRetries?: number; @@ -76,6 +77,7 @@ export interface devicesConfig extends device { external?: boolean; refreshRate?: number; updateRate?: number; + pushRate?: number; firmware?: string; logging?: string; connectionType?: string; diff --git a/src/utils.ts b/src/utils.ts index 265cc934..34df7a3a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -93,10 +93,10 @@ export function convertUnits(value: number, unit: string, convert?: string): num export function rgb2hs(r: any, g: any, b: any) { -/** - * Credit: - * https://github.com/WickyNilliams/pure-color -**/ + /** + * Credit: + * https://github.com/WickyNilliams/pure-color + **/ r = parseInt(r); g = parseInt(g); b = parseInt(b);