From 303fdc662d86bc77de378438ae5d2461a680aa6d Mon Sep 17 00:00:00 2001 From: Luligu Date: Sat, 30 Nov 2024 18:05:52 +0100 Subject: [PATCH] Release 1.2.0 --- CHANGELOG.md | 8 +- README.md | 16 ++-- package-lock.json | 195 ++++++++++++++++++++++--------------------- package.json | 16 ++-- src/index.test.ts | 8 +- src/platform.test.ts | 2 +- src/platform.ts | 176 +++++++++++++++++++++++--------------- 7 files changed, 241 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e37c478..065d083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,18 @@ If you like this project and find it useful, please consider giving it a star on All notable changes to this project will be documented in this file. -## [1.2.0] - 2024-11-21 +## [1.2.0] - 2024-11-30 ### Added -- [edge]: Verified to work with matterbridge edge (matter.js new API). +- [edge]: Verified to work with Matterbridge edge (matter.js new API). +- [plugin]: Refactor movement to support concurrent movementes from all screens. +- [plugin]: Refactor movement to show the movement on the controller (if it supports that) even for close and open commands. - [matter]: Added bridgedNode and powerSource device types to the cover. ### Changed -- [package]: Requires matterbridge 1.6.0. +- [package]: Requires matterbridge 1.6.5. - [package]: Updated dependencies. ### Fixed diff --git a/README.md b/README.md index 1176864..610d398 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ Follow these steps to install or update Matterbridge if it is not already instal on Windows: ``` -npm install -g matterbridge +npm install -g matterbridge --omit=dev ``` -on Linux (you need the necessary permissions): +on Linux (if may need the necessary permissions): ``` -sudo npm install -g matterbridge +sudo npm install -g matterbridge --omit=dev ``` See the complete guidelines on [Matterbridge](https://github.com/Luligu/matterbridge/blob/main/README.md) for more information. @@ -43,11 +43,15 @@ A working setup of any of the TaHoma bridges (like the Connectivity kit). ## How to install +Open the frontend of matterbridge, select the plugin and install it. + +## How to install from the command line + On windows: ``` cd $HOME\Matterbridge -npm install -g matterbridge-somfy-tahoma +npm install -g matterbridge-somfy-tahoma --omit=dev matterbridge -add matterbridge-somfy-tahoma ``` @@ -55,14 +59,14 @@ On linux: ``` cd ~/Matterbridge -sudo npm install -g matterbridge-somfy-tahoma +sudo npm install -g matterbridge-somfy-tahoma --omit=dev matterbridge -add matterbridge-somfy-tahoma ``` Then start Matterbridge ``` -matterbridge -bridge +matterbridge ``` ## How to use it diff --git a/package-lock.json b/package-lock.json index 348964c..f7b904f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,19 +14,19 @@ "overkiz-client": "1.0.20" }, "devDependencies": { - "@eslint/js": "9.15.0", + "@eslint/js": "9.16.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.1", - "eslint": "9.15.0", + "@types/node": "22.10.1", + "eslint": "9.16.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", - "prettier": "3.3.3", + "prettier": "3.4.1", "ts-jest": "29.2.5", - "typescript": "5.6.3", - "typescript-eslint": "8.15.0" + "typescript": "5.7.2", + "typescript-eslint": "8.16.0" }, "engines": { "node": ">=18.0.0" @@ -658,9 +658,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "dev": true, "license": "MIT", "engines": { @@ -1425,13 +1425,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", - "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/stack-utils": { @@ -1459,17 +1459,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", - "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", + "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/type-utils": "8.15.0", - "@typescript-eslint/utils": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/type-utils": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1493,16 +1493,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", - "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", + "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "debug": "^4.3.4" }, "engines": { @@ -1522,14 +1522,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", - "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", + "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1540,14 +1540,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", - "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", + "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/utils": "8.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1568,9 +1568,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", - "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", + "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", "dev": true, "license": "MIT", "engines": { @@ -1582,14 +1582,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", - "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", + "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1637,16 +1637,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", - "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", + "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1665,13 +1665,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", - "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", + "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/types": "8.16.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1805,9 +1805,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2095,9 +2095,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001683", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", - "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { @@ -2456,9 +2456,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", - "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==", + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", "dev": true, "license": "ISC" }, @@ -2537,9 +2537,9 @@ } }, "node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", + "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2548,7 +2548,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", + "@eslint/js": "9.16.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3193,12 +3193,15 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.1.0.tgz", + "integrity": "sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3" + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3498,13 +3501,15 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", + "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "gopd": "^1.1.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4892,9 +4897,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, "license": "MIT", "bin": { @@ -5470,9 +5475,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", "engines": { @@ -5575,9 +5580,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5589,15 +5594,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz", - "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", + "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.15.0", - "@typescript-eslint/parser": "8.15.0", - "@typescript-eslint/utils": "8.15.0" + "@typescript-eslint/eslint-plugin": "8.16.0", + "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/utils": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5616,9 +5621,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index f25a90a..c36ff9f 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "start:frontend": "matterbridge", "start:bridge": "matterbridge -bridge", "start:childbridge": "matterbridge -childbridge", - "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --verbose", + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", "test:verbose": "node --experimental-vm-modules node_modules/jest/bin/jest.js --verbose", "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch", "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage", @@ -76,18 +76,18 @@ "overkiz-client": "1.0.20" }, "devDependencies": { - "@eslint/js": "9.15.0", + "@eslint/js": "9.16.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.1", - "eslint": "9.15.0", + "@types/node": "22.10.1", + "eslint": "9.16.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", - "prettier": "3.3.3", + "prettier": "3.4.1", "ts-jest": "29.2.5", - "typescript": "5.6.3", - "typescript-eslint": "8.15.0" + "typescript": "5.7.2", + "typescript-eslint": "8.16.0" } -} \ No newline at end of file +} diff --git a/src/index.test.ts b/src/index.test.ts index 3a71768..96d6913 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -15,7 +15,7 @@ describe('initializePlugin', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.0', + matterbridgeVersion: '1.6.5', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; @@ -35,4 +35,10 @@ describe('initializePlugin', () => { expect(result).toBeInstanceOf(SomfyTahomaPlatform); }); + + it('should shutdown the instance of TestPlatform', () => { + const result = initializePlugin(mockMatterbridge, mockLog, mockConfig); + + expect(result).toBeInstanceOf(SomfyTahomaPlatform); + }); }); diff --git a/src/platform.test.ts b/src/platform.test.ts index 9287b0e..469b3fc 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -17,7 +17,7 @@ describe('TestPlatform', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.0', + matterbridgeVersion: '1.6.5', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; diff --git a/src/platform.ts b/src/platform.ts index a568aa2..3b62729 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,5 +1,4 @@ import { - DeviceTypes, PlatformConfig, WindowCovering, WindowCoveringCluster, @@ -11,6 +10,8 @@ import { EndpointOptions, bridgedNode, powerSource, + MatterbridgeEndpoint, + coverDevice, } from 'matterbridge'; import { AnsiLogger, BLUE, debugStringify, dn, rs, wr } from 'matterbridge/logger'; import { isValidNumber } from 'matterbridge/utils'; @@ -18,15 +19,26 @@ import { NodeStorageManager } from 'matterbridge/storage'; import { Action, Client, Command, Device, Execution } from 'overkiz-client'; import path from 'path'; -import { db, YELLOW } from 'node-ansi-logger'; +import { CYAN, db, ign, nf, YELLOW } from 'node-ansi-logger'; type MovementDuration = Record; +const Stopped = WindowCovering.MovementStatus.Stopped; +const Opening = WindowCovering.MovementStatus.Opening; +const Closing = WindowCovering.MovementStatus.Closing; + +interface Cover { + tahomaDevice: Device; + bridgedDevice: MatterbridgeDevice; + movementDuration: number; + movementStatus: WindowCovering.MovementStatus; + moveInterval?: NodeJS.Timeout; + commandTimeout?: NodeJS.Timeout; +} export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { private tahomaDevices: Device[] = []; private bridgedDevices: MatterbridgeDevice[] = []; - moveInterval: NodeJS.Timeout | undefined = undefined; - commandTimeout: NodeJS.Timeout | undefined = undefined; + private covers = new Map(); // NodeStorageManager private nodeStorageManager: NodeStorageManager; @@ -43,12 +55,8 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { async createMutableDevice(definition: DeviceTypeDefinition | AtLeastOne, options: EndpointOptions = {}, debug = false): Promise { let device: MatterbridgeDevice; - const matterbridge = await import('matterbridge'); - if ('edge' in this.matterbridge && this.matterbridge.edge === true && 'MatterbridgeEndpoint' in matterbridge) { - // Dynamically resolve the MatterbridgeEndpoint class from the imported module and instantiate it without throwing a TypeScript error for old versions of Matterbridge - // eslint-disable-next-line @typescript-eslint/no-explicit-any - device = new (matterbridge as any).MatterbridgeEndpoint(definition, options, debug) as MatterbridgeDevice; - } else device = new MatterbridgeDevice(definition, undefined, debug); + if (this.matterbridge.edge === true) device = new MatterbridgeEndpoint(definition, options, debug) as unknown as MatterbridgeDevice; + else device = new MatterbridgeDevice(definition, undefined, debug); return device; } @@ -56,9 +64,9 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { super(matterbridge, log, config); // Verify that Matterbridge is the correct version - if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.0')) { + if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.5')) { throw new Error( - `This plugin requires Matterbridge version >= "1.6.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`, + `This plugin requires Matterbridge version >= "1.6.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`, ); } @@ -126,8 +134,15 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { return; } - // Set cover to target = current position and status to stopped (current position is persisted in the cluster) - for (const device of this.bridgedDevices) await device.setWindowCoveringTargetAsCurrentAndStopped(); + // Set cover to target = current position and status to stopped (current position persists in the cluster) + for (const device of this.bridgedDevices) { + const cover = this.covers.get(device.deviceName ?? ''); + const position = await device.getAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', device.log); + cover?.bridgedDevice.log.info( + `Setting ${device.deviceName} target to ${CYAN}${position / 100}%${nf} position and status to stopped. Movement duration: ${CYAN}${cover?.movementDuration}${nf}`, + ); + await device.setWindowCoveringTargetAsCurrentAndStopped(); + } } override async onShutdown(reason?: string) { @@ -138,10 +153,13 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { this.tahomaClient.removeAllListeners(); } this.tahomaClient = undefined; - clearInterval(this.moveInterval); - this.moveInterval = undefined; - clearTimeout(this.commandTimeout); - this.commandTimeout = undefined; + this.covers.forEach((cover) => { + clearInterval(cover.moveInterval); + cover.moveInterval = undefined; + clearTimeout(cover.commandTimeout); + cover.commandTimeout = undefined; + }); + this.covers.clear(); if (this.config.unregisterOnShutdown === true) await this.unregisterAllDevices(); } @@ -229,7 +247,7 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { this.log.debug(`- states ${debugStringify(device.states)}`); this.log.debug(`- duration ${duration}`); - const cover = await this.createMutableDevice(DeviceTypes.WINDOW_COVERING, { uniqueStorageKey: device.label }, this.config.debug as boolean); + const cover = await this.createMutableDevice(coverDevice, { uniqueStorageKey: device.label }, this.config.debug as boolean); cover.createDefaultIdentifyClusterServer(); cover.createDefaultGroupsClusterServer(); cover.createDefaultScenesClusterServer(); @@ -240,86 +258,112 @@ export class SomfyTahomaPlatform extends MatterbridgeDynamicPlatform { cover.createDefaultPowerSourceWiredClusterServer(); await this.registerDevice(cover); this.bridgedDevices.push(cover); + this.covers.set(device.label, { tahomaDevice: device, bridgedDevice: cover, movementStatus: Stopped, movementDuration: duration }); cover.addCommandHandler('identify', async ({ request: { identifyTime } }) => { - this.log.info(`Command identify called identifyTime:${identifyTime}`); + this.log.info(`Command ${ign}identify${rs}${nf} called identifyTime:${identifyTime}`); await this.sendCommand('identify', device, true); }); - cover.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => { - if (this.commandTimeout) clearTimeout(this.commandTimeout); - this.commandTimeout = setTimeout(async () => { - this.commandTimeout = undefined; - this.log.info(`Command goToLiftPercentage called liftPercent100thsValue:${liftPercent100thsValue}`); - await this.moveToPosition(cover, device, liftPercent100thsValue, duration); - }, 1000); - }); - cover.addCommandHandler('upOrOpen', async () => { - this.log.info('Command upOrOpen called'); - await this.sendCommand('open', device, true); - clearInterval(this.moveInterval); - await cover.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped); + const cover = this.covers.get(device.label); + if (!cover) return; + cover.bridgedDevice.log.info(`Command ${ign}upOrOpen${rs}${nf} called for ${CYAN}${cover.tahomaDevice.label}`); + await this.moveToPosition(cover, 0); + /* + clearInterval(cover.moveInterval); + cover.movementStatus = Stopped; + await cover.bridgedDevice.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped); + await this.sendCommand('open', cover.tahomaDevice, true); + */ }); cover.addCommandHandler('downOrClose', async () => { - this.log.info('Command downOrClose called'); - await this.sendCommand('close', device, true); - clearInterval(this.moveInterval); - await cover.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped); + const cover = this.covers.get(device.label); + if (!cover) return; + cover.bridgedDevice.log.info(`Command ${ign}downOrClose${rs}${nf} called for ${CYAN}${cover.tahomaDevice.label}`); + await this.moveToPosition(cover, 10000); + /* + clearInterval(cover.moveInterval); + cover.movementStatus = Stopped; + await cover.bridgedDevice.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped); + await this.sendCommand('close', cover.tahomaDevice, true); + */ }); cover.addCommandHandler('stopMotion', async () => { - this.log.info('Command stopMotion called'); - if ((await cover.getWindowCoveringStatus()) !== WindowCovering.MovementStatus.Stopped) { - await this.sendCommand('stop', device, true); + const cover = this.covers.get(device.label); + if (!cover) return; + cover.bridgedDevice.log.info(`Command ${ign}stopMotion${rs}${nf} called for ${CYAN}${cover.tahomaDevice.label}. Status ${cover.movementStatus}`); + clearInterval(cover.moveInterval); + if (cover.movementStatus !== WindowCovering.MovementStatus.Stopped) { + await this.sendCommand('stop', cover.tahomaDevice, true); } - clearInterval(this.moveInterval); - await cover.setWindowCoveringTargetAsCurrentAndStopped(); + cover.movementStatus = Stopped; + await cover.bridgedDevice.setWindowCoveringTargetAsCurrentAndStopped(); + }); + + cover.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => { + const cover = this.covers.get(device.label); + if (!cover) return; + await cover.bridgedDevice.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', liftPercent100thsValue, cover.bridgedDevice.log); + if (cover.commandTimeout) clearTimeout(cover.commandTimeout); + cover.commandTimeout = setTimeout(async () => { + cover.commandTimeout = undefined; + cover.bridgedDevice.log.info(`Command ${ign}goToLiftPercentage${rs}${nf} ${CYAN}${liftPercent100thsValue}${nf} called for ${CYAN}${cover.tahomaDevice.label}`); + await this.moveToPosition(cover, liftPercent100thsValue); + }, 1000); }); } } // With Matter 0=open 10000=close - private async moveToPosition(cover: MatterbridgeDevice, tahomaDevice: Device, targetPosition: number, fullMovementSeconds = 30) { + private async moveToPosition(cover: Cover, targetPosition: number) { + const log = cover.bridgedDevice.log; + let currentPosition = cover.bridgedDevice.getAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', log); + if (!isValidNumber(currentPosition, 0, 10000)) return; + log.info(`Moving from ${currentPosition} to ${targetPosition}...`); + // Stop movement if already moving - if ((await cover.getWindowCoveringStatus()) !== WindowCovering.MovementStatus.Stopped) { - this.log.info('*Stopping movement.'); - clearInterval(this.moveInterval); - await cover.setWindowCoveringTargetAsCurrentAndStopped(); - await this.sendCommand('stop', tahomaDevice, true); + if ((await cover.movementStatus) !== Stopped) { + log.info('*Stopping current movement.'); + clearInterval(cover.moveInterval); + await cover.bridgedDevice.setWindowCoveringTargetAsCurrentAndStopped(); + await this.sendCommand('stop', cover.tahomaDevice, true); + cover.movementStatus = Stopped; return; } // Return if already at target position - let currentPosition = cover.getAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', cover.log); - if (!isValidNumber(currentPosition, 0, 10000)) return; if (targetPosition === currentPosition) { - clearInterval(this.moveInterval); - await cover.setWindowCoveringTargetAsCurrentAndStopped(); - this.log.info(`*Moving from ${currentPosition} to ${targetPosition}. No movement needed.`); + clearInterval(cover.moveInterval); + await cover.bridgedDevice.setWindowCoveringTargetAsCurrentAndStopped(); + cover.movementStatus = Stopped; + log.info(`*Moving from ${currentPosition} to ${targetPosition}. No movement needed.`); return; } // Start movement const movement = targetPosition - currentPosition; - const movementSeconds = Math.abs((movement * fullMovementSeconds) / 10000); - this.log.debug(`*Moving from ${currentPosition} to ${targetPosition} in ${movementSeconds} seconds. Movement requested ${movement}`); - cover.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', targetPosition, cover.log); + const movementSeconds = Math.abs((movement * cover.movementDuration) / 10000); + log.debug(`*Moving from ${currentPosition} to ${targetPosition} in ${movementSeconds} seconds. Movement requested ${movement}`); + cover.bridgedDevice.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', targetPosition, log); - await cover.setWindowCoveringStatus(targetPosition > currentPosition ? WindowCovering.MovementStatus.Closing : WindowCovering.MovementStatus.Opening); - await this.sendCommand(targetPosition > currentPosition ? 'close' : 'open', tahomaDevice, true); + await cover.bridgedDevice.setWindowCoveringStatus(targetPosition > currentPosition ? WindowCovering.MovementStatus.Closing : WindowCovering.MovementStatus.Opening); + cover.movementStatus = targetPosition > currentPosition ? Closing : Opening; + await this.sendCommand(targetPosition > currentPosition ? 'close' : 'open', cover.tahomaDevice, true); - this.moveInterval = setInterval(async () => { - this.log.debug(`**Moving interval from ${currentPosition} to ${targetPosition} with movement ${movement}`); + cover.moveInterval = setInterval(async () => { + log.debug(`**Moving interval from ${currentPosition} to ${targetPosition} with movement ${movement}`); if (currentPosition === null) return; currentPosition = Math.round(currentPosition + movement / movementSeconds); if (Math.abs(targetPosition - currentPosition) <= 100 || (movement > 0 && currentPosition >= targetPosition) || (movement < 0 && currentPosition <= targetPosition)) { - clearInterval(this.moveInterval); - await cover.setWindowCoveringCurrentTargetStatus(targetPosition, targetPosition, WindowCovering.MovementStatus.Stopped); - if (targetPosition !== 0 && targetPosition !== 10000) await this.sendCommand('stop', tahomaDevice, true); - this.log.debug(`*Moving stopped at ${targetPosition}`); + clearInterval(cover.moveInterval); + await cover.bridgedDevice.setWindowCoveringCurrentTargetStatus(targetPosition, targetPosition, WindowCovering.MovementStatus.Stopped); + cover.movementStatus = Stopped; + if (targetPosition !== 0 && targetPosition !== 10000) await this.sendCommand('stop', cover.tahomaDevice, true); + log.debug(`*Moving stopped at ${targetPosition}`); } else { - this.log.debug(`*Moving from ${currentPosition} to ${targetPosition} difference ${Math.abs(targetPosition - currentPosition)}`); - await cover.setAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', Math.max(0, Math.min(currentPosition, 10000)), cover.log); + log.debug(`*Moving from ${currentPosition} to ${targetPosition} difference ${Math.abs(targetPosition - currentPosition)}`); + await cover.bridgedDevice.setAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', Math.max(0, Math.min(currentPosition, 10000)), log); } }, 1000); }