diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e7cb3e..e128635 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge-shelly and sponsoring it.
+## [1.0.5] - 2024-10-21
+
+### Added
+
+- [shelly]: Verified support for shellyhtg3 (Shelly H&T Gen 3) with new firmware 1.4.5-gbf870ca.
+- [shelly]: Added event awake to save sleepy device cache file.
+- [shelly]: Added scheduled_restart event from Sys to set the devices that will not be loaded from cache at restart.
+- [shelly]: Added config_changed event from Sys to set the devices that will not be loaded from cache at restart.
+- [shelly]: Added ota_begin, ota_progress and ota_success events from Sys to set the devices that will not be loaded from cache at restart.
+- [shelly]: Added ColorTemp to Shelly Duo and Shelly Bulb in white mode.
+- [cockpit]: Added cockpit dashboard 1.0
+
+
+
+
+
+
## [1.0.4] - 2024-10-17
### Added
diff --git a/eslint.config.js b/eslint.config.js
index f2e03c1..51ac8a3 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -8,7 +8,7 @@ import eslintPluginPrettier from 'eslint-plugin-prettier/recommended';
export default [
{
name: 'global ignores',
- ignores: ['dist/', 'build/', 'node_modules/', 'coverage/', 'frontend/'],
+ ignores: ['dist/', 'build/', 'node_modules/', 'coverage/', 'frontend/', 'rock-s0/'],
},
eslint.configs.recommended,
...tseslint.configs.strict,
diff --git a/package-lock.json b/package-lock.json
index 8ee67cc..bf1b744 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "matterbridge-shelly",
- "version": "1.0.4",
+ "version": "1.0.5-dev.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "matterbridge-shelly",
- "version": "1.0.4",
+ "version": "1.0.5-dev.4",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -747,9 +747,9 @@
}
},
"node_modules/@eslint/core": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz",
- "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
+ "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
@@ -804,9 +804,9 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz",
- "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz",
+ "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
@@ -1724,21 +1724,417 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@typescript-eslint/eslint-plugin": {
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.9.0.tgz",
+ "integrity": "sha512-Y1n621OCy4m7/vTXNlCbMVp87zSd7NH0L9cXD8aIpOaNlzeWxIK4+Q19A68gSmTNRZn92UjocVUWDthGxtqHFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.9.0",
+ "@typescript-eslint/type-utils": "8.9.0",
+ "@typescript-eslint/utils": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz",
+ "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz",
+ "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz",
+ "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "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.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.9.0.tgz",
+ "integrity": "sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.9.0",
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/typescript-estree": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz",
+ "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/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,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/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,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.9.0.tgz",
+ "integrity": "sha512-U+BLn2rqTTHnc4FL3FJjxaXptTxmf9sNftJK62XLz4+GxG3hLHm/SUNaaXP5Y4uTiuYoL5YLy4JBCJe3+t8awQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.9.0",
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/typescript-estree": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz",
+ "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz",
+ "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz",
+ "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "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.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz",
+ "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/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,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/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,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz",
+ "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.10.0",
+ "@typescript-eslint/visitor-keys": "8.10.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.9.0.tgz",
+ "integrity": "sha512-JD+/pCqlKqAk5961vxCluK+clkppHY07IbV3vett97KOV+8C6l+CPEPwpUuiMwgbOz/qrN3Ke4zzjqbT+ls+1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.9.0",
+ "@typescript-eslint/utils": "8.9.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "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/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz",
+ "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz",
+ "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.9.0.tgz",
- "integrity": "sha512-Y1n621OCy4m7/vTXNlCbMVp87zSd7NH0L9cXD8aIpOaNlzeWxIK4+Q19A68gSmTNRZn92UjocVUWDthGxtqHFg==",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz",
+ "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-2-Clause",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.9.0",
- "@typescript-eslint/type-utils": "8.9.0",
- "@typescript-eslint/utils": "8.9.0",
+ "@typescript-eslint/types": "8.9.0",
"@typescript-eslint/visitor-keys": "8.9.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.3.1",
- "natural-compare": "^1.4.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
@@ -1748,28 +2144,23 @@
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
- "eslint": "^8.57.0 || ^9.0.0"
- },
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
- "node_modules/@typescript-eslint/parser": {
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
"version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.9.0.tgz",
- "integrity": "sha512-U+BLn2rqTTHnc4FL3FJjxaXptTxmf9sNftJK62XLz4+GxG3hLHm/SUNaaXP5Y4uTiuYoL5YLy4JBCJe3+t8awQ==",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.9.0.tgz",
+ "integrity": "sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ==",
"dev": true,
- "license": "BSD-2-Clause",
+ "license": "MIT",
"dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.9.0",
"@typescript-eslint/types": "8.9.0",
- "@typescript-eslint/typescript-estree": "8.9.0",
- "@typescript-eslint/visitor-keys": "8.9.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/typescript-estree": "8.9.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1780,22 +2171,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
}
},
- "node_modules/@typescript-eslint/scope-manager": {
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz",
- "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz",
+ "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.9.0",
- "@typescript-eslint/visitor-keys": "8.9.0"
+ "eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1805,35 +2191,49 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.9.0.tgz",
- "integrity": "sha512-JD+/pCqlKqAk5961vxCluK+clkppHY07IbV3vett97KOV+8C6l+CPEPwpUuiMwgbOz/qrN3Ke4zzjqbT+ls+1Q==",
+ "node_modules/@typescript-eslint/type-utils/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,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.9.0",
- "@typescript-eslint/utils": "8.9.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^1.3.0"
- },
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-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,
+ "license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz",
- "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz",
+ "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1845,14 +2245,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz",
- "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz",
+ "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "8.9.0",
- "@typescript-eslint/visitor-keys": "8.9.0",
+ "@typescript-eslint/types": "8.10.0",
+ "@typescript-eslint/visitor-keys": "8.10.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -1900,16 +2300,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.9.0.tgz",
- "integrity": "sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz",
+ "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.9.0",
- "@typescript-eslint/types": "8.9.0",
- "@typescript-eslint/typescript-estree": "8.9.0"
+ "@typescript-eslint/scope-manager": "8.10.0",
+ "@typescript-eslint/types": "8.10.0",
+ "@typescript-eslint/typescript-estree": "8.10.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1923,13 +2323,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz",
- "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz",
+ "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/types": "8.10.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -2699,9 +3099,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.40",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.40.tgz",
- "integrity": "sha512-LYm78o6if4zTasnYclgQzxEcgMoIcybWOhkATWepN95uwVVWV0/IW10v+2sIeHE+bIYWipLneTftVyQm45UY7g==",
+ "version": "1.5.41",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz",
+ "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==",
"dev": true,
"license": "ISC"
},
@@ -2760,9 +3160,9 @@
}
},
"node_modules/eslint": {
- "version": "9.12.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz",
- "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==",
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
+ "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -2770,9 +3170,9 @@
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0",
"@eslint/config-array": "^0.18.0",
- "@eslint/core": "^0.6.0",
+ "@eslint/core": "^0.7.0",
"@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.12.0",
+ "@eslint/js": "9.13.0",
"@eslint/plugin-kit": "^0.2.0",
"@humanfs/node": "^0.16.5",
"@humanwhocodes/module-importer": "^1.0.1",
@@ -2923,6 +3323,17 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint/node_modules/@eslint/js": {
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
+ "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/espree": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz",
@@ -5918,6 +6329,147 @@
}
}
},
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz",
+ "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz",
+ "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz",
+ "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/visitor-keys": "8.9.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "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.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.9.0.tgz",
+ "integrity": "sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.9.0",
+ "@typescript-eslint/types": "8.9.0",
+ "@typescript-eslint/typescript-estree": "8.9.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz",
+ "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.9.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/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,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/typescript-eslint/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,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
diff --git a/package.json b/package.json
index 43b91c6..dbd229f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matterbridge-shelly",
- "version": "1.0.4",
+ "version": "1.0.5-dev.4",
"description": "Matterbridge shelly plugin",
"author": "https://github.com/Luligu",
"license": "Apache-2.0",
@@ -127,4 +127,4 @@
"typescript": "5.6.3",
"typescript-eslint": "8.9.0"
}
-}
+}
\ No newline at end of file
diff --git a/rock-s0/INSTALL.md b/rock-s0/INSTALL.md
index 5327168..f562799 100644
--- a/rock-s0/INSTALL.md
+++ b/rock-s0/INSTALL.md
@@ -114,7 +114,9 @@ sudo apt upgrade
# Install matterbridge cockpit plugin
-copy the four files from cockpit directory to "\usr\share\cockpit\matterbridge"
+Create the directory "\usr\share\cockpit\matterbridge"
+
+copy all the files from cockpit directory to "\usr\share\cockpit\matterbridge"
# Prevent the journal logs to grow
diff --git a/rock-s0/cockpit/index.html b/rock-s0/cockpit/index.html
index 3baa0ae..cd7946a 100644
--- a/rock-s0/cockpit/index.html
+++ b/rock-s0/cockpit/index.html
@@ -3,24 +3,49 @@
- Matterbridge Management
+ Matterbridge Dashboard
+
Matterbridge Dashboard
-
-
Loading Matterbridge status...
-
Loading Matterbridge current version...
-
Loading Matterbridge latest version...
-
Loading Shelly plugin current version...
-
Loading Shelly plugin latest version...
-
System logs:
-
Fetching logs...
+
+
+
+
System
+
Loading Matterbridge status...
+
WebSocket:
+
Last seen:
+
+
+
+
+
+
+
Matterbridge
+
Loading current version...
+
Loading latest version...
+
+
+
+
+
+
Shelly plugin
+
Loading current version...
+
Loading latest version...
+
+
+
+
+
diff --git a/rock-s0/cockpit/manifest.json b/rock-s0/cockpit/manifest.json
index c8b1545..145f913 100644
--- a/rock-s0/cockpit/manifest.json
+++ b/rock-s0/cockpit/manifest.json
@@ -7,5 +7,5 @@
"label": "Matterbridge"
}
},
- "content-security-policy": "default-src 'self'; script-src 'self'; style-src 'self';"
+ "content-security-policy": "default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self';"
}
\ No newline at end of file
diff --git a/rock-s0/cockpit/matterbridge.css b/rock-s0/cockpit/matterbridge.css
index e67bb56..90ef8c5 100644
--- a/rock-s0/cockpit/matterbridge.css
+++ b/rock-s0/cockpit/matterbridge.css
@@ -1,18 +1,82 @@
-#content {
- font-family: Arial, sans-serif;
+/* Dark mode */
+body {
+ --main-bg-color: #26292d;
+ --main-text-color: #ffffff;
+ --div-bg-color: #1b1d21;
+ --div-text-color: #e0e0e0;
+ --button-bg-color: #0066cc;
+ --button-text-color: #e0e0e0;
+ background-color: var(--main-bg-color);
+ color: var(--main-text-color);
+}
+
+h1 {
+ font-size: 24px;
+}
+
+.div-panels {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-between;
+}
+
+.div-panel {
+ color: var(--div-text-color);
+ background-color: var(--div-bg-color);
+ border-radius: 5px;
+ font-size: 14px;
padding: 20px;
+ margin-top: 10px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ min-width: 220px;
+}
+
+.div-title {
+ color: var(--div-text-color);
+ font-size: 20px;
+ padding: 10px;
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.div-button {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.div-qrcode {
+ margin-bottom: 10px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
button {
- padding: 5px 10px;
+ padding: 6px 16px;
margin: 10px 0;
+ color: var(--button-text-color);
+ background-color: var(--button-bg-color);
+ border-radius: 3px;
+ font-size: 16px;
+ border: none;
+}
+
+#content {
+ font-family: Arial, sans-serif;
+ padding: 20px;
}
#status,
#matterbridge-current,
#matterbridge-latest,
#shelly-current,
-#shelly-latest,
-#logs {
+#shelly-latest {
margin: 10px 0;
}
\ No newline at end of file
diff --git a/rock-s0/cockpit/matterbridge.js b/rock-s0/cockpit/matterbridge.js
index 7396280..61edb37 100644
--- a/rock-s0/cockpit/matterbridge.js
+++ b/rock-s0/cockpit/matterbridge.js
@@ -9,11 +9,11 @@ cockpit.transport.wait(function () {
cockpit
.spawn(['systemctl', 'is-active', 'matterbridge'])
.then(function (status) {
- document.getElementById('status').innerText = `Status: ${status.trim().replace('\n', '')}`;
+ document.getElementById('matterbridge-status').innerText = `Service: ${status.trim().replace('\n', '')}`;
})
.catch(function (error) {
console.error('Error fetching Matterbridge status:', error);
- document.getElementById('status').innerText = 'Error fetching status.';
+ document.getElementById('matterbridge-status').innerText = 'Error fetching system service status.';
});
}
@@ -55,7 +55,7 @@ cockpit.transport.wait(function () {
// Extract the version number using a regular expression
const versionMatch = status.match(/matterbridge-shelly@(\d+\.\d+\.\d+)/);
const version = versionMatch ? versionMatch[1] : 'Unknown';
- document.getElementById('shelly-current').innerText = `Shelly plugin current version: ${version}`;
+ document.getElementById('shelly-current').innerText = `Current version: ${version}`;
})
.catch(function (error) {
console.error('Error fetching Shelly plugin current version:', error);
@@ -69,7 +69,8 @@ cockpit.transport.wait(function () {
.spawn(['npm', 'show', 'matterbridge-shelly', 'version'])
// cockpit.spawn(["whoami"])
.then(function (status) {
- document.getElementById('shelly-latest').innerText = `Shelly plugin latest version: ${status.trim()}`;
+ document.getElementById('shelly-latest').innerText = `Latest version: ${status.trim()}`;
+ showQR();
})
.catch(function (error) {
console.error('Error fetching Shelly plugin latest version:', error);
@@ -77,6 +78,31 @@ cockpit.transport.wait(function () {
});
}
+ // Show the QR code
+ function showQR() {
+ const qrText = "https://github.com/Luligu/matterbridge.git";
+
+ // Check if the qrcode element exists
+ const qrCodeElement = document.getElementById("qrcode");
+ if (qrCodeElement) {
+ console.log("Generating QR code...");
+
+ // Generate the QR code and insert it into the #qrcode div
+ new QRCode(qrCodeElement, {
+ text: qrText,
+ width: 128, // Width of the QR code (you can adjust this)
+ height: 128, // Height of the QR code (you can adjust this)
+ colorDark: "#000000", // Dark color of the QR code
+ colorLight: "#ffffff", // Light color of the QR code (background)
+ correctLevel: QRCode.CorrectLevel.H // Error correction level
+ });
+
+ console.log("QR code generated successfully!");
+ } else {
+ console.error("QR code element not found!");
+ }
+ }
+
// Fetch logs
function fetchLogs() {
cockpit
@@ -92,18 +118,51 @@ cockpit.transport.wait(function () {
});
}
- // Reload the Matterbridge configuration
- document.getElementById('frontend-button').addEventListener('click', function () {
+ // Open the frontend
+ document.getElementById('frontend-button')?.addEventListener('click', function () {
const hostname = window.location.hostname;
const newUrl = `http://${hostname}:8283`;
window.open(newUrl, '_blank');
});
+ // Install matterbridge
+ document.getElementById('matterbridge-update')?.addEventListener('click', function () {
+ console.log('Updating matterbridge...');
+ document.getElementById('matterbridge-current').innerText = `Updating...`;
+ cockpit
+ .spawn(['sudo', 'npm', 'install', '-g', 'matterbridge', '--omit=dev'])
+ .then(function (logs) {
+ console.log('Updated matterbridge:', logs);
+ fetchMatterbridgeCurrent();
+ })
+ .catch(function (error) {
+ console.error('Error updating matterbridge:', error);
+ document.getElementById('matterbridge-current').innerText = `Error updating...`;
+ });
+ });
+
+ // Install matterbridge-shelly
+ document.getElementById('shelly-update')?.addEventListener('click', function () {
+ console.log('Updating matterbridge-shelly...');
+ document.getElementById('shelly-current').innerText = `Updating...`;
+ cockpit
+ .spawn(['sudo', 'npm', 'install', '-g', 'matterbridge-shelly', '--omit=dev'])
+ .then(function (logs) {
+ console.log('Updated matterbridge-shelly:', logs);
+ fetchShellyCurrent();
+ })
+ .catch(function (error) {
+ console.error('Error updating matterbridge-shelly:', error);
+ document.getElementById('shelly-current').innerText = `Error updating...`;
+ });
+ });
+
// Initial fetch of status and logs
fetchStatus();
fetchMatterbridgeCurrent();
fetchMatterbridgeLatest();
fetchShellyCurrent();
fetchShellyLatest();
- fetchLogs();
+ // fetchLogs();
+
});
diff --git a/rock-s0/cockpit/qrcode.js b/rock-s0/cockpit/qrcode.js
new file mode 100644
index 0000000..b7d4639
--- /dev/null
+++ b/rock-s0/cockpit/qrcode.js
@@ -0,0 +1 @@
+var QRCode; !function () { function a(a) { this.mode = c.MODE_8BIT_BYTE, this.data = a, this.parsedData = []; for (var b = [], d = 0, e = this.data.length; e > d; d++) { var f = this.data.charCodeAt(d); f > 65536 ? (b[0] = 240 | (1835008 & f) >>> 18, b[1] = 128 | (258048 & f) >>> 12, b[2] = 128 | (4032 & f) >>> 6, b[3] = 128 | 63 & f) : f > 2048 ? (b[0] = 224 | (61440 & f) >>> 12, b[1] = 128 | (4032 & f) >>> 6, b[2] = 128 | 63 & f) : f > 128 ? (b[0] = 192 | (1984 & f) >>> 6, b[1] = 128 | 63 & f) : b[0] = f, this.parsedData = this.parsedData.concat(b) } this.parsedData.length != this.data.length && (this.parsedData.unshift(191), this.parsedData.unshift(187), this.parsedData.unshift(239)) } function b(a, b) { this.typeNumber = a, this.errorCorrectLevel = b, this.modules = null, this.moduleCount = 0, this.dataCache = null, this.dataList = [] } function i(a, b) { if (void 0 == a.length) throw new Error(a.length + "/" + b); for (var c = 0; c < a.length && 0 == a[c];)c++; this.num = new Array(a.length - c + b); for (var d = 0; d < a.length - c; d++)this.num[d] = a[d + c] } function j(a, b) { this.totalCount = a, this.dataCount = b } function k() { this.buffer = [], this.length = 0 } function m() { return "undefined" != typeof CanvasRenderingContext2D } function n() { var a = !1, b = navigator.userAgent; return /android/i.test(b) && (a = !0, aMat = b.toString().match(/android ([0-9]\.[0-9])/i), aMat && aMat[1] && (a = parseFloat(aMat[1]))), a } function r(a, b) { for (var c = 1, e = s(a), f = 0, g = l.length; g >= f; f++) { var h = 0; switch (b) { case d.L: h = l[f][0]; break; case d.M: h = l[f][1]; break; case d.Q: h = l[f][2]; break; case d.H: h = l[f][3] }if (h >= e) break; c++ } if (c > l.length) throw new Error("Too long data"); return c } function s(a) { var b = encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g, "a"); return b.length + (b.length != a ? 3 : 0) } a.prototype = { getLength: function () { return this.parsedData.length }, write: function (a) { for (var b = 0, c = this.parsedData.length; c > b; b++)a.put(this.parsedData[b], 8) } }, b.prototype = { addData: function (b) { var c = new a(b); this.dataList.push(c), this.dataCache = null }, isDark: function (a, b) { if (0 > a || this.moduleCount <= a || 0 > b || this.moduleCount <= b) throw new Error(a + "," + b); return this.modules[a][b] }, getModuleCount: function () { return this.moduleCount }, make: function () { this.makeImpl(!1, this.getBestMaskPattern()) }, makeImpl: function (a, c) { this.moduleCount = 4 * this.typeNumber + 17, this.modules = new Array(this.moduleCount); for (var d = 0; d < this.moduleCount; d++) { this.modules[d] = new Array(this.moduleCount); for (var e = 0; e < this.moduleCount; e++)this.modules[d][e] = null } this.setupPositionProbePattern(0, 0), this.setupPositionProbePattern(this.moduleCount - 7, 0), this.setupPositionProbePattern(0, this.moduleCount - 7), this.setupPositionAdjustPattern(), this.setupTimingPattern(), this.setupTypeInfo(a, c), this.typeNumber >= 7 && this.setupTypeNumber(a), null == this.dataCache && (this.dataCache = b.createData(this.typeNumber, this.errorCorrectLevel, this.dataList)), this.mapData(this.dataCache, c) }, setupPositionProbePattern: function (a, b) { for (var c = -1; 7 >= c; c++)if (!(-1 >= a + c || this.moduleCount <= a + c)) for (var d = -1; 7 >= d; d++)-1 >= b + d || this.moduleCount <= b + d || (this.modules[a + c][b + d] = c >= 0 && 6 >= c && (0 == d || 6 == d) || d >= 0 && 6 >= d && (0 == c || 6 == c) || c >= 2 && 4 >= c && d >= 2 && 4 >= d ? !0 : !1) }, getBestMaskPattern: function () { for (var a = 0, b = 0, c = 0; 8 > c; c++) { this.makeImpl(!0, c); var d = f.getLostPoint(this); (0 == c || a > d) && (a = d, b = c) } return b }, createMovieClip: function (a, b, c) { var d = a.createEmptyMovieClip(b, c), e = 1; this.make(); for (var f = 0; f < this.modules.length; f++)for (var g = f * e, h = 0; h < this.modules[f].length; h++) { var i = h * e, j = this.modules[f][h]; j && (d.beginFill(0, 100), d.moveTo(i, g), d.lineTo(i + e, g), d.lineTo(i + e, g + e), d.lineTo(i, g + e), d.endFill()) } return d }, setupTimingPattern: function () { for (var a = 8; a < this.moduleCount - 8; a++)null == this.modules[a][6] && (this.modules[a][6] = 0 == a % 2); for (var b = 8; b < this.moduleCount - 8; b++)null == this.modules[6][b] && (this.modules[6][b] = 0 == b % 2) }, setupPositionAdjustPattern: function () { for (var a = f.getPatternPosition(this.typeNumber), b = 0; b < a.length; b++)for (var c = 0; c < a.length; c++) { var d = a[b], e = a[c]; if (null == this.modules[d][e]) for (var g = -2; 2 >= g; g++)for (var h = -2; 2 >= h; h++)this.modules[d + g][e + h] = -2 == g || 2 == g || -2 == h || 2 == h || 0 == g && 0 == h ? !0 : !1 } }, setupTypeNumber: function (a) { for (var b = f.getBCHTypeNumber(this.typeNumber), c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[Math.floor(c / 3)][c % 3 + this.moduleCount - 8 - 3] = d } for (var c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[c % 3 + this.moduleCount - 8 - 3][Math.floor(c / 3)] = d } }, setupTypeInfo: function (a, b) { for (var c = this.errorCorrectLevel << 3 | b, d = f.getBCHTypeInfo(c), e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 6 > e ? this.modules[e][8] = g : 8 > e ? this.modules[e + 1][8] = g : this.modules[this.moduleCount - 15 + e][8] = g } for (var e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 8 > e ? this.modules[8][this.moduleCount - e - 1] = g : 9 > e ? this.modules[8][15 - e - 1 + 1] = g : this.modules[8][15 - e - 1] = g } this.modules[this.moduleCount - 8][8] = !a }, mapData: function (a, b) { for (var c = -1, d = this.moduleCount - 1, e = 7, g = 0, h = this.moduleCount - 1; h > 0; h -= 2)for (6 == h && h--; ;) { for (var i = 0; 2 > i; i++)if (null == this.modules[d][h - i]) { var j = !1; g < a.length && (j = 1 == (1 & a[g] >>> e)); var k = f.getMask(b, d, h - i); k && (j = !j), this.modules[d][h - i] = j, e--, -1 == e && (g++, e = 7) } if (d += c, 0 > d || this.moduleCount <= d) { d -= c, c = -c; break } } } }, b.PAD0 = 236, b.PAD1 = 17, b.createData = function (a, c, d) { for (var e = j.getRSBlocks(a, c), g = new k, h = 0; h < d.length; h++) { var i = d[h]; g.put(i.mode, 4), g.put(i.getLength(), f.getLengthInBits(i.mode, a)), i.write(g) } for (var l = 0, h = 0; h < e.length; h++)l += e[h].dataCount; if (g.getLengthInBits() > 8 * l) throw new Error("code length overflow. (" + g.getLengthInBits() + ">" + 8 * l + ")"); for (g.getLengthInBits() + 4 <= 8 * l && g.put(0, 4); 0 != g.getLengthInBits() % 8;)g.putBit(!1); for (; ;) { if (g.getLengthInBits() >= 8 * l) break; if (g.put(b.PAD0, 8), g.getLengthInBits() >= 8 * l) break; g.put(b.PAD1, 8) } return b.createBytes(g, e) }, b.createBytes = function (a, b) { for (var c = 0, d = 0, e = 0, g = new Array(b.length), h = new Array(b.length), j = 0; j < b.length; j++) { var k = b[j].dataCount, l = b[j].totalCount - k; d = Math.max(d, k), e = Math.max(e, l), g[j] = new Array(k); for (var m = 0; m < g[j].length; m++)g[j][m] = 255 & a.buffer[m + c]; c += k; var n = f.getErrorCorrectPolynomial(l), o = new i(g[j], n.getLength() - 1), p = o.mod(n); h[j] = new Array(n.getLength() - 1); for (var m = 0; m < h[j].length; m++) { var q = m + p.getLength() - h[j].length; h[j][m] = q >= 0 ? p.get(q) : 0 } } for (var r = 0, m = 0; m < b.length; m++)r += b[m].totalCount; for (var s = new Array(r), t = 0, m = 0; d > m; m++)for (var j = 0; j < b.length; j++)m < g[j].length && (s[t++] = g[j][m]); for (var m = 0; e > m; m++)for (var j = 0; j < b.length; j++)m < h[j].length && (s[t++] = h[j][m]); return s }; for (var c = { MODE_NUMBER: 1, MODE_ALPHA_NUM: 2, MODE_8BIT_BYTE: 4, MODE_KANJI: 8 }, d = { L: 1, M: 0, Q: 3, H: 2 }, e = { PATTERN000: 0, PATTERN001: 1, PATTERN010: 2, PATTERN011: 3, PATTERN100: 4, PATTERN101: 5, PATTERN110: 6, PATTERN111: 7 }, f = { PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]], G15: 1335, G18: 7973, G15_MASK: 21522, getBCHTypeInfo: function (a) { for (var b = a << 10; f.getBCHDigit(b) - f.getBCHDigit(f.G15) >= 0;)b ^= f.G15 << f.getBCHDigit(b) - f.getBCHDigit(f.G15); return (a << 10 | b) ^ f.G15_MASK }, getBCHTypeNumber: function (a) { for (var b = a << 12; f.getBCHDigit(b) - f.getBCHDigit(f.G18) >= 0;)b ^= f.G18 << f.getBCHDigit(b) - f.getBCHDigit(f.G18); return a << 12 | b }, getBCHDigit: function (a) { for (var b = 0; 0 != a;)b++, a >>>= 1; return b }, getPatternPosition: function (a) { return f.PATTERN_POSITION_TABLE[a - 1] }, getMask: function (a, b, c) { switch (a) { case e.PATTERN000: return 0 == (b + c) % 2; case e.PATTERN001: return 0 == b % 2; case e.PATTERN010: return 0 == c % 3; case e.PATTERN011: return 0 == (b + c) % 3; case e.PATTERN100: return 0 == (Math.floor(b / 2) + Math.floor(c / 3)) % 2; case e.PATTERN101: return 0 == b * c % 2 + b * c % 3; case e.PATTERN110: return 0 == (b * c % 2 + b * c % 3) % 2; case e.PATTERN111: return 0 == (b * c % 3 + (b + c) % 2) % 2; default: throw new Error("bad maskPattern:" + a) } }, getErrorCorrectPolynomial: function (a) { for (var b = new i([1], 0), c = 0; a > c; c++)b = b.multiply(new i([1, g.gexp(c)], 0)); return b }, getLengthInBits: function (a, b) { if (b >= 1 && 10 > b) switch (a) { case c.MODE_NUMBER: return 10; case c.MODE_ALPHA_NUM: return 9; case c.MODE_8BIT_BYTE: return 8; case c.MODE_KANJI: return 8; default: throw new Error("mode:" + a) } else if (27 > b) switch (a) { case c.MODE_NUMBER: return 12; case c.MODE_ALPHA_NUM: return 11; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 10; default: throw new Error("mode:" + a) } else { if (!(41 > b)) throw new Error("type:" + b); switch (a) { case c.MODE_NUMBER: return 14; case c.MODE_ALPHA_NUM: return 13; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 12; default: throw new Error("mode:" + a) } } }, getLostPoint: function (a) { for (var b = a.getModuleCount(), c = 0, d = 0; b > d; d++)for (var e = 0; b > e; e++) { for (var f = 0, g = a.isDark(d, e), h = -1; 1 >= h; h++)if (!(0 > d + h || d + h >= b)) for (var i = -1; 1 >= i; i++)0 > e + i || e + i >= b || (0 != h || 0 != i) && g == a.isDark(d + h, e + i) && f++; f > 5 && (c += 3 + f - 5) } for (var d = 0; b - 1 > d; d++)for (var e = 0; b - 1 > e; e++) { var j = 0; a.isDark(d, e) && j++, a.isDark(d + 1, e) && j++, a.isDark(d, e + 1) && j++, a.isDark(d + 1, e + 1) && j++, (0 == j || 4 == j) && (c += 3) } for (var d = 0; b > d; d++)for (var e = 0; b - 6 > e; e++)a.isDark(d, e) && !a.isDark(d, e + 1) && a.isDark(d, e + 2) && a.isDark(d, e + 3) && a.isDark(d, e + 4) && !a.isDark(d, e + 5) && a.isDark(d, e + 6) && (c += 40); for (var e = 0; b > e; e++)for (var d = 0; b - 6 > d; d++)a.isDark(d, e) && !a.isDark(d + 1, e) && a.isDark(d + 2, e) && a.isDark(d + 3, e) && a.isDark(d + 4, e) && !a.isDark(d + 5, e) && a.isDark(d + 6, e) && (c += 40); for (var k = 0, e = 0; b > e; e++)for (var d = 0; b > d; d++)a.isDark(d, e) && k++; var l = Math.abs(100 * k / b / b - 50) / 5; return c += 10 * l } }, g = { glog: function (a) { if (1 > a) throw new Error("glog(" + a + ")"); return g.LOG_TABLE[a] }, gexp: function (a) { for (; 0 > a;)a += 255; for (; a >= 256;)a -= 255; return g.EXP_TABLE[a] }, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256) }, h = 0; 8 > h; h++)g.EXP_TABLE[h] = 1 << h; for (var h = 8; 256 > h; h++)g.EXP_TABLE[h] = g.EXP_TABLE[h - 4] ^ g.EXP_TABLE[h - 5] ^ g.EXP_TABLE[h - 6] ^ g.EXP_TABLE[h - 8]; for (var h = 0; 255 > h; h++)g.LOG_TABLE[g.EXP_TABLE[h]] = h; i.prototype = { get: function (a) { return this.num[a] }, getLength: function () { return this.num.length }, multiply: function (a) { for (var b = new Array(this.getLength() + a.getLength() - 1), c = 0; c < this.getLength(); c++)for (var d = 0; d < a.getLength(); d++)b[c + d] ^= g.gexp(g.glog(this.get(c)) + g.glog(a.get(d))); return new i(b, 0) }, mod: function (a) { if (this.getLength() - a.getLength() < 0) return this; for (var b = g.glog(this.get(0)) - g.glog(a.get(0)), c = new Array(this.getLength()), d = 0; d < this.getLength(); d++)c[d] = this.get(d); for (var d = 0; d < a.getLength(); d++)c[d] ^= g.gexp(g.glog(a.get(d)) + b); return new i(c, 0).mod(a) } }, j.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]], j.getRSBlocks = function (a, b) { var c = j.getRsBlockTable(a, b); if (void 0 == c) throw new Error("bad rs block @ typeNumber:" + a + "/errorCorrectLevel:" + b); for (var d = c.length / 3, e = [], f = 0; d > f; f++)for (var g = c[3 * f + 0], h = c[3 * f + 1], i = c[3 * f + 2], k = 0; g > k; k++)e.push(new j(h, i)); return e }, j.getRsBlockTable = function (a, b) { switch (b) { case d.L: return j.RS_BLOCK_TABLE[4 * (a - 1) + 0]; case d.M: return j.RS_BLOCK_TABLE[4 * (a - 1) + 1]; case d.Q: return j.RS_BLOCK_TABLE[4 * (a - 1) + 2]; case d.H: return j.RS_BLOCK_TABLE[4 * (a - 1) + 3]; default: return void 0 } }, k.prototype = { get: function (a) { var b = Math.floor(a / 8); return 1 == (1 & this.buffer[b] >>> 7 - a % 8) }, put: function (a, b) { for (var c = 0; b > c; c++)this.putBit(1 == (1 & a >>> b - c - 1)) }, getLengthInBits: function () { return this.length }, putBit: function (a) { var b = Math.floor(this.length / 8); this.buffer.length <= b && this.buffer.push(0), a && (this.buffer[b] |= 128 >>> this.length % 8), this.length++ } }; var l = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]], o = function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { function g(a, b) { var c = document.createElementNS("http://www.w3.org/2000/svg", a); for (var d in b) b.hasOwnProperty(d) && c.setAttribute(d, b[d]); return c } var b = this._htOption, c = this._el, d = a.getModuleCount(); Math.floor(b.width / d), Math.floor(b.height / d), this.clear(); var h = g("svg", { viewBox: "0 0 " + String(d) + " " + String(d), width: "100%", height: "100%", fill: b.colorLight }); h.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"), c.appendChild(h), h.appendChild(g("rect", { fill: b.colorDark, width: "1", height: "1", id: "template" })); for (var i = 0; d > i; i++)for (var j = 0; d > j; j++)if (a.isDark(i, j)) { var k = g("use", { x: String(i), y: String(j) }); k.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template"), h.appendChild(k) } }, a.prototype.clear = function () { for (; this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild) }, a }(), p = "svg" === document.documentElement.tagName.toLowerCase(), q = p ? o : m() ? function () { function a() { this._elImage.src = this._elCanvas.toDataURL("image/png"), this._elImage.style.display = "block", this._elCanvas.style.display = "none" } function d(a, b) { var c = this; if (c._fFail = b, c._fSuccess = a, null === c._bSupportDataURI) { var d = document.createElement("img"), e = function () { c._bSupportDataURI = !1, c._fFail && _fFail.call(c) }, f = function () { c._bSupportDataURI = !0, c._fSuccess && c._fSuccess.call(c) }; return d.onabort = e, d.onerror = e, d.onload = f, d.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", void 0 } c._bSupportDataURI === !0 && c._fSuccess ? c._fSuccess.call(c) : c._bSupportDataURI === !1 && c._fFail && c._fFail.call(c) } if (this._android && this._android <= 2.1) { var b = 1 / window.devicePixelRatio, c = CanvasRenderingContext2D.prototype.drawImage; CanvasRenderingContext2D.prototype.drawImage = function (a, d, e, f, g, h, i, j) { if ("nodeName" in a && /img/i.test(a.nodeName)) for (var l = arguments.length - 1; l >= 1; l--)arguments[l] = arguments[l] * b; else "undefined" == typeof j && (arguments[1] *= b, arguments[2] *= b, arguments[3] *= b, arguments[4] *= b); c.apply(this, arguments) } } var e = function (a, b) { this._bIsPainted = !1, this._android = n(), this._htOption = b, this._elCanvas = document.createElement("canvas"), this._elCanvas.width = b.width, this._elCanvas.height = b.height, a.appendChild(this._elCanvas), this._el = a, this._oContext = this._elCanvas.getContext("2d"), this._bIsPainted = !1, this._elImage = document.createElement("img"), this._elImage.style.display = "none", this._el.appendChild(this._elImage), this._bSupportDataURI = null }; return e.prototype.draw = function (a) { var b = this._elImage, c = this._oContext, d = this._htOption, e = a.getModuleCount(), f = d.width / e, g = d.height / e, h = Math.round(f), i = Math.round(g); b.style.display = "none", this.clear(); for (var j = 0; e > j; j++)for (var k = 0; e > k; k++) { var l = a.isDark(j, k), m = k * f, n = j * g; c.strokeStyle = l ? d.colorDark : d.colorLight, c.lineWidth = 1, c.fillStyle = l ? d.colorDark : d.colorLight, c.fillRect(m, n, f, g), c.strokeRect(Math.floor(m) + .5, Math.floor(n) + .5, h, i), c.strokeRect(Math.ceil(m) - .5, Math.ceil(n) - .5, h, i) } this._bIsPainted = !0 }, e.prototype.makeImage = function () { this._bIsPainted && d.call(this, a) }, e.prototype.isPainted = function () { return this._bIsPainted }, e.prototype.clear = function () { this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height), this._bIsPainted = !1 }, e.prototype.round = function (a) { return a ? Math.floor(1e3 * a) / 1e3 : a }, e }() : function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { for (var b = this._htOption, c = this._el, d = a.getModuleCount(), e = Math.floor(b.width / d), f = Math.floor(b.height / d), g = [''], h = 0; d > h; h++) { g.push(""); for (var i = 0; d > i; i++)g.push(' | '); g.push("
") } g.push("
"), c.innerHTML = g.join(""); var j = c.childNodes[0], k = (b.width - j.offsetWidth) / 2, l = (b.height - j.offsetHeight) / 2; k > 0 && l > 0 && (j.style.margin = l + "px " + k + "px") }, a.prototype.clear = function () { this._el.innerHTML = "" }, a }(); QRCode = function (a, b) { if (this._htOption = { width: 256, height: 256, typeNumber: 4, colorDark: "#000000", colorLight: "#ffffff", correctLevel: d.H }, "string" == typeof b && (b = { text: b }), b) for (var c in b) this._htOption[c] = b[c]; "string" == typeof a && (a = document.getElementById(a)), this._android = n(), this._el = a, this._oQRCode = null, this._oDrawing = new q(this._el, this._htOption), this._htOption.text && this.makeCode(this._htOption.text) }, QRCode.prototype.makeCode = function (a) { this._oQRCode = new b(r(a, this._htOption.correctLevel), this._htOption.correctLevel), this._oQRCode.addData(a), this._oQRCode.make(), this._el.title = a, this._oDrawing.draw(this._oQRCode), this.makeImage() }, QRCode.prototype.makeImage = function () { "function" == typeof this._oDrawing.makeImage && (!this._android || this._android >= 3) && this._oDrawing.makeImage() }, QRCode.prototype.clear = function () { this._oDrawing.clear() }, QRCode.CorrectLevel = d }();
\ No newline at end of file
diff --git a/src/coapServer.ts b/src/coapServer.ts
index 158d00f..6e88e0f 100644
--- a/src/coapServer.ts
+++ b/src/coapServer.ts
@@ -384,12 +384,14 @@ export class CoapServer extends EventEmitter {
// light component
if (s.D === 'output') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'state', range: s.R });
- if (s.D === 'brightness') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'brightness', range: s.R });
- if (s.D === 'gain') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'brightness', range: s.R });
+ if (s.D === 'brightness') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'brightness', range: s.R }); // Used by white channels
+ if (s.D === 'gain') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'gain', range: s.R }); // Used by color channels
if (s.D === 'red') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'red', range: s.R });
if (s.D === 'green') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'green', range: s.R });
if (s.D === 'blue') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'blue', range: s.R });
if (s.D === 'white') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'white', range: s.R });
+ if (s.D === 'whiteLevel') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'white', range: s.R });
+ if (s.D === 'colorTemp') desc.push({ id: s.I, component: b.D.replace('_', ':'), property: 'temp', range: s.R });
if (s.D === 'power' && b.D.startsWith('light')) desc.push({ id: s.I, component: 'meter:0', property: 'power', range: s.R });
if (s.D === 'energy' && b.D.startsWith('light')) desc.push({ id: s.I, component: 'meter:0', property: 'total', range: s.R });
diff --git a/src/platform.ts b/src/platform.ts
index eed316e..90bae29 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -528,13 +528,36 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
// Scan the device components
for (const [key, component] of device) {
if (component.name === 'Sys') {
- component.on('event', (component: string, event: string) => {
+ component.on('event', (component: string, event: string, data: ShellyData) => {
this.log.debug(`Received event ${event} from component ${component}`);
// scheduled_restart is for restart and for reset
if (event === 'scheduled_restart') {
if (!device.sleepMode) this.changedDevices.set(device.id, device.id);
- device.log.notice(`Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} is restarting`);
- device.log.notice(`If you changed the configuration on shelly device ${idn}${device.name}${rs}${nt}, please restart matterbridge for the change to take effect.`);
+ device.log.notice(
+ `Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} is restarting in ${CYAN}${data.time_ms}${nt} ms`,
+ );
+ device.log.notice(`If the configuration on shelly device ${idn}${device.name}${rs}${nt} has changed, please restart matterbridge for the change to take effect.`);
+ }
+ if (event === 'config_changed') {
+ if (!device.sleepMode) this.changedDevices.set(device.id, device.id);
+ device.log.notice(
+ `Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} sent config changed rev: ${CYAN}${data.cfg_rev}${nt}`,
+ );
+ device.log.notice(`If the configuration on shelly device ${idn}${device.name}${rs}${nt} has changed, please restart matterbridge for the change to take effect.`);
+ }
+ if (event === 'ota_begin') {
+ if (!device.sleepMode) this.changedDevices.set(device.id, device.id);
+ device.log.notice(`Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} is starting OTA`);
+ }
+ if (event === 'ota_progress') {
+ device.log.notice(
+ `Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} OTA is progressing: ${CYAN}${data.progress_percent}${nt}%`,
+ );
+ }
+ if (event === 'ota_success') {
+ if (!device.sleepMode) this.changedDevices.set(device.id, device.id);
+ device.log.notice(`Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} finished succesfully OTA`);
+ device.log.notice(`The firmware on shelly device ${idn}${device.name}${rs}${nt} has changed, please restart matterbridge for the change to take effect.`);
}
if (event === 'sleep') {
device.log.notice(`Shelly device ${idn}${device.name}${rs}${nt} id ${hk}${device.id}${nt} host ${zb}${device.host}${nt} is sleeping`);
@@ -552,13 +575,16 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
}
if (
(lightComponent.hasProperty('red') && lightComponent.hasProperty('green') && lightComponent.hasProperty('blue') && device.profile !== 'white') ||
+ (lightComponent.hasProperty('temp') && device.profile !== 'color') ||
lightComponent.hasProperty('rgb')
) {
deviceType = DeviceTypes.COLOR_TEMPERATURE_LIGHT;
clusterIds.push(ColorControl.Cluster.id);
}
const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [deviceType], clusterIds);
- mbDevice.configureColorControlCluster(true, false, false, ColorControl.ColorMode.CurrentHueAndCurrentSaturation, child);
+ if (lightComponent.hasProperty('temp') && device.profile !== 'color')
+ mbDevice.configureColorControlCluster(false, false, true, ColorControl.ColorMode.ColorTemperatureMireds, child);
+ else mbDevice.configureColorControlCluster(true, false, false, ColorControl.ColorMode.CurrentHueAndCurrentSaturation, child);
// Add the electrical measurementa cluster on the same endpoint
this.addElectricalMeasurements(mbDevice, child, device, lightComponent);
@@ -587,7 +613,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
const level = child.getClusterServer(LevelControlCluster)?.getCurrentLevelAttribute();
const saturation = child.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.getCurrentSaturationAttribute() ?? 0;
const rgb = hslColorToRgbColor((request.hue / 254) * 360, (saturation / 254) * 100, 50);
- this.log.warn(`***Sending command moveToHue => ColorRGB(${rgb.r}, ${rgb.g}, ${rgb.b})`);
+ this.log.warn(`Sending command moveToHue => ColorRGB(${rgb.r}, ${rgb.g}, ${rgb.b})`);
if (device.colorCommandTimeout) clearTimeout(device.colorCommandTimeout);
device.colorCommandTimeout = setTimeout(() => {
this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'ColorRGB', state, level, { r: rgb.r, g: rgb.g, b: rgb.b });
@@ -599,7 +625,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
const level = child.getClusterServer(LevelControlCluster)?.getCurrentLevelAttribute();
const hue = child.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.getCurrentHueAttribute() ?? 0;
const rgb = hslColorToRgbColor((hue / 254) * 360, (request.saturation / 254) * 100, 50);
- this.log.warn(`***Sending command moveToSaturation => ColorRGB(${rgb.r}, ${rgb.g}, ${rgb.b})`);
+ this.log.warn(`Sending command moveToSaturation => ColorRGB(${rgb.r}, ${rgb.g}, ${rgb.b})`);
if (device.colorCommandTimeout) clearTimeout(device.colorCommandTimeout);
device.colorCommandTimeout = setTimeout(() => {
this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'ColorRGB', state, level, { r: rgb.r, g: rgb.g, b: rgb.b });
@@ -613,6 +639,13 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'ColorRGB', state, level, { r: rgb.r, g: rgb.g, b: rgb.b });
});
+ mbDevice.addCommandHandler('moveToColorTemperature', async ({ request, attributes, endpoint }) => {
+ attributes.colorMode.setLocal(ColorControl.ColorMode.ColorTemperatureMireds);
+ const state = child.getClusterServer(OnOffCluster)?.getOnOffAttribute();
+ const level = child.getClusterServer(LevelControlCluster)?.getCurrentLevelAttribute();
+ this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'ColorTemp', state, level, undefined, request.colorTemperatureMireds);
+ });
+
// Add event handler from Shelly
lightComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
this.shellyUpdateHandler(mbDevice, device, component, property, value);
@@ -632,12 +665,6 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
// Add the electrical measurementa cluster on the same endpoint
this.addElectricalMeasurements(mbDevice, child, device, switchComponent);
- /*
- // Set the OnOff attribute
- const state = switchComponent.getValue('state');
- if (isValidBoolean(state)) child.getClusterServer(OnOffCluster)?.setOnOffAttribute(state);
- */
-
// Add command handlers
mbDevice.addCommandHandler('on', async (data) => {
this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'On', true);
@@ -662,19 +689,6 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
// Add the electrical measurementa cluster on the same endpoint
this.addElectricalMeasurements(mbDevice, child, device, coverComponent);
- // Set the WindowCovering attributes
- /*
- "positioning": true, // Gen 1 devices when positioning control is enabled (even if it is not calibrated)
- "pos_control": true, // Gen 2 devices
- "current_pos": 0 // Gen 1 and 2 devices 0-100
- const position = coverComponent.hasProperty('current_pos') ? coverComponent.getValue('current_pos') : undefined;
- if (isValidNumber(position, 0, 100)) {
- const matterPos = 10000 - Math.min(Math.max(Math.round(position * 100), 0), 10000);
- child.getClusterServer(WindowCovering.Complete)?.setCurrentPositionLiftPercent100thsAttribute(matterPos);
- }
- mbDevice.setWindowCoveringTargetAsCurrentAndStopped(child);
- */
-
// Add command handlers
mbDevice.addCommandHandler('upOrOpen', async (data) => {
this.shellyCoverCommandHandler(mbDevice, data.endpoint.number, device, 'Open', 0);
@@ -697,20 +711,16 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
}
} else if (component.name === 'PowerMeter' && config.exposePowerMeter !== 'disabled') {
const pmComponent = device.getComponent(key);
- if (pmComponent) {
- if (config.exposePowerMeter === 'matter13') {
- // Add the Matter 1.3 electricalSensor device type with the ElectricalPowerMeasurement and ElectricalEnergyMeasurement clusters
- const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [electricalSensor], [ElectricalPowerMeasurement.Cluster.id, ElectricalEnergyMeasurement.Cluster.id]);
-
- device.log.debug(
- `Added ElectricalPowerMeasurement and ElectricalEnergyMeasurement clusters to endpoint ${hk}${child.name}${db} component ${hk}${component.name}:${component.id}${db}`,
- );
-
- // Update the electrical attributes
- for (const property of component.properties) {
- if (!['voltage', 'current', 'power', 'apower', 'act_power', 'total', 'aenergy'].includes(property.key)) continue;
- this.shellyUpdateHandler(mbDevice, device, component.id, property.key, property.value);
- }
+ if (pmComponent && config.exposePowerMeter === 'matter13') {
+ // Add the Matter 1.3 electricalSensor device type with the ElectricalPowerMeasurement and ElectricalEnergyMeasurement clusters
+ const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [electricalSensor], [ElectricalPowerMeasurement.Cluster.id, ElectricalEnergyMeasurement.Cluster.id]);
+ device.log.debug(
+ `Added ElectricalPowerMeasurement and ElectricalEnergyMeasurement clusters to endpoint ${hk}${child.name}${db} component ${hk}${component.name}:${component.id}${db}`,
+ );
+ // Update the electrical attributes
+ for (const property of component.properties) {
+ if (!['voltage', 'current', 'power', 'apower', 'act_power', 'total', 'aenergy'].includes(property.key)) continue;
+ this.shellyUpdateHandler(mbDevice, device, component.id, property.key, property.value);
}
// Add event handler
pmComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
@@ -721,7 +731,6 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
const inputComponent = device.getComponent(key);
// Skip the input component if it is disabled in Gen 2/3 devices
if (inputComponent && inputComponent?.hasProperty('enable') && inputComponent?.getValue('enable') === false) continue;
-
if (
inputComponent &&
inputComponent?.hasProperty('state') &&
@@ -1260,6 +1269,9 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
mbDevice.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, shellyDevice.log, childEndpoint);
}
}
+ if (lightComponent.hasProperty('temp') && shellyDevice.profile !== 'color') {
+ mbDevice.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, shellyDevice.log, childEndpoint);
+ }
if (lightComponent.hasProperty('rgb') && shellyDevice.profile !== 'white') {
const rgb = lightComponent.getValue('rgb') as object;
if (isValidArray(rgb, 3, 3) && isValidNumber(rgb[0], 0, 255) && isValidNumber(rgb[1], 0, 255) && isValidNumber(rgb[2], 0, 255)) {
@@ -1430,7 +1442,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
device = await ShellyDevice.create(this.shelly, log, host);
if (device) {
this.log.info(`*Created Shelly device ${hk}${deviceId}${nf} host ${zb}${host}${nf}`);
- await device.saveDevicePayloads(path.join(this.matterbridge.matterbridgePluginDirectory, 'matterbridge-shelly'));
+ await device.saveDevicePayloads(this.shelly.dataPath);
}
}
if (!device) {
@@ -1450,6 +1462,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
state?: boolean,
level?: number | null,
color?: { r: number; g: number; b: number },
+ colorTemp?: number,
): boolean {
// Get the matter endpoint
if (!endpointNumber) {
@@ -1461,7 +1474,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
shellyDevice.log.error(`shellyCommandHandler error: endpoint not found for shelly device ${dn}${shellyDevice?.id}${er}`);
return false;
}
- // Get the Shelly switch component
+ // Get the Shelly component
const componentName = endpoint.uniqueStorageKey;
if (!componentName) {
shellyDevice.log.error(`shellyCommandHandler error: componentName not found for shelly device ${dn}${shellyDevice?.id}${er}`);
@@ -1496,6 +1509,23 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
`${db}Sent command ${hk}${componentName}${nf}:ColorRGB(${YELLOW}${color.r}${nf}, ${YELLOW}${color.g}${nf}, ${YELLOW}${color.b}${nf})${db} to shelly device ${idn}${shellyDevice?.id}${rs}${db}`,
);
}
+
+ // Send ColorTemp() command
+ if (command === 'ColorTemp' && isValidNumber(colorTemp, 147, 500)) {
+ const minColorTemp = 147;
+ const maxColorTemp = 500;
+ const minTemp = shellyDevice.model === 'SHBDUO-1' ? 2700 : 3000;
+ const maxTemp = 6500;
+ const temp = Math.max(Math.min(Math.round(((colorTemp - minColorTemp) / (maxColorTemp - minColorTemp)) * (minTemp - maxTemp) + maxTemp), maxTemp), minTemp);
+ const lightComponent = shellyDevice?.getComponent(componentName) as ShellyLightComponent;
+ if (shellyDevice.gen === 1)
+ ShellyDevice.fetch(shellyDevice.shelly, shellyDevice.log, shellyDevice.host, `${lightComponent.id.slice(0, lightComponent.id.indexOf(':'))}/${lightComponent.index}`, {
+ temp,
+ });
+ shellyDevice.log.info(
+ `${db}Sent command ${hk}${componentName}${nf}:ColorTemp(for model ${shellyDevice.model} ${YELLOW}${colorTemp}->${temp}${nf})${db} to shelly device ${idn}${shellyDevice?.id}${rs}${db}`,
+ );
+ }
return true;
}
@@ -1583,7 +1613,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
matterbridgeDevice.setAttribute(OnOffCluster.id, 'onOff', value, shellyDevice.log, endpoint);
}
// Update brightness
- if (isLightComponent(shellyComponent) && property === 'brightness' && isValidNumber(value, 0, 100)) {
+ if (isLightComponent(shellyComponent) && (property === 'gain' || property === 'brightness') && isValidNumber(value, 0, 100)) {
matterbridgeDevice.setAttribute(LevelControlCluster.id, 'currentLevel', Math.max(Math.min(Math.round((value / 100) * 255), 255), 0), shellyDevice.log, endpoint);
}
// Update color gen 1
@@ -1602,6 +1632,21 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
matterbridgeDevice.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, shellyDevice.log, endpoint);
}, 200);
}
+ // Update colorTemp gen 1
+ if (isLightComponent(shellyComponent) && property === 'temp' && isValidNumber(value, 2700, 6500)) {
+ const minValue = shellyDevice.model === 'SHBDUO-1' ? 2700 : 3000;
+ const maxValue = 6500;
+ const minMatterTemp = 147;
+ const maxMatterTemp = 500;
+ const matterTemp = Math.max(
+ Math.min(Math.round(((value - minValue) / (maxValue - minValue)) * (minMatterTemp - maxMatterTemp) + maxMatterTemp), maxMatterTemp),
+ minMatterTemp,
+ );
+ this.log.debug(`ColorTemp for ${shellyDevice.model}: colorTemperature:${value} => colorTemperatureMireds:${matterTemp}`);
+ matterbridgeDevice.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, shellyDevice.log, endpoint);
+ matterbridgeDevice.setAttribute(ColorControlCluster.id, 'enhancedColorMode', ColorControl.EnhancedColorMode.ColorTemperatureMireds, shellyDevice.log, endpoint);
+ matterbridgeDevice.setAttribute(ColorControlCluster.id, 'colorTemperatureMireds', matterTemp, shellyDevice.log, endpoint);
+ }
// Update color gen 2/3
if (
isLightComponent(shellyComponent) &&
diff --git a/src/shelly.ts b/src/shelly.ts
index 7c73126..bccefe8 100644
--- a/src/shelly.ts
+++ b/src/shelly.ts
@@ -79,6 +79,7 @@ export class Shelly extends EventEmitter {
}
// this.log.debug(`Received wssupdate from device id ${hk}${shellyId}${db} host ${zb}${device.host}${db}:${rs}\n`, params);
this.log.debug(`Received wssupdate from device id ${hk}${shellyId}${db} host ${zb}${device.host}${db}`);
+ if (device.sleepMode) device.emit('awake');
if (!device.online) {
device.online = true;
device.emit('online');
@@ -100,6 +101,7 @@ export class Shelly extends EventEmitter {
}
// this.log.debug(`Received wssevent from device id ${hk}${shellyId}${db} host ${zb}${device.host}${db}:${rs}\n`, params);
this.log.debug(`Received wssevent from device id ${hk}${shellyId}${db} host ${zb}${device.host}${db}`);
+ if (device.sleepMode) device.emit('awake');
if (!device.online) {
device.online = true;
device.emit('online');
@@ -211,6 +213,15 @@ export class Shelly extends EventEmitter {
if (this.coapServer) this.coapServer.dataPath = path;
}
+ /**
+ * Gets the data path for the Shelly instance.
+ *
+ * @returns {string} The current data path.
+ */
+ get dataPath(): string {
+ return this._dataPath;
+ }
+
/**
* Sets the debug mode for mDNS scanning.
*
diff --git a/src/shellyComponent.ts b/src/shellyComponent.ts
index 80452b1..74e4734 100644
--- a/src/shellyComponent.ts
+++ b/src/shellyComponent.ts
@@ -71,6 +71,11 @@ export function isCoverComponent(component: ShellyComponent | undefined): compon
return ['Cover', 'Roller'].includes(component.name);
}
+interface ShellyComponentEvent {
+ update: [component: string, key: string, data: ShellyDataType];
+ event: [component: string, event: string, data: ShellyData];
+}
+
/**
* Rappresents the ShellyComponent class.
*/
@@ -107,20 +112,16 @@ export class ShellyComponent extends EventEmitter {
// Extend the ShellyComponent class prototype to include the Switch Relay Light methods dynamically
if (isSwitchComponent(this) || isLightComponent(this)) {
this.On = function () {
- // this.setValue('state', true);
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { turn: 'on' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Set`, { id: this.index, on: true });
};
this.Off = function () {
- // this.setValue('state', false);
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { turn: 'off' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Set`, { id: this.index, on: false });
};
this.Toggle = function () {
- // const currentState = this.getValue('state');
- // this.setValue('state', !currentState);
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { turn: 'toggle' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Toggle`, { id: this.index });
};
@@ -131,7 +132,6 @@ export class ShellyComponent extends EventEmitter {
this.Level = function (level: number) {
if (!this.hasProperty('brightness')) return;
const adjustedLevel = Math.min(Math.max(Math.round(level), 0), 100);
- // this.setValue('brightness', adjustedLevel);
if (device.gen === 1 && this.hasProperty('brightness'))
ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { brightness: adjustedLevel });
if (device.gen === 1 && this.hasProperty('gain'))
@@ -144,9 +144,6 @@ export class ShellyComponent extends EventEmitter {
red = Math.min(Math.max(Math.round(red), 0), 255);
green = Math.min(Math.max(Math.round(green), 0), 255);
blue = Math.min(Math.max(Math.round(blue), 0), 255);
- // this.setValue('red', red);
- // this.setValue('green', green);
- // this.setValue('blue', blue);
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { red, green, blue });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Set`, { id: this.index, red, green, blue });
}
@@ -154,7 +151,6 @@ export class ShellyComponent extends EventEmitter {
red = Math.min(Math.max(Math.round(red), 0), 255);
green = Math.min(Math.max(Math.round(green), 0), 255);
blue = Math.min(Math.max(Math.round(blue), 0), 255);
- // this.setValue('rgb', [red, green, blue]);
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { red, green, blue });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Set`, { id: this.index, rgb: [red, green, blue] as any });
@@ -165,19 +161,16 @@ export class ShellyComponent extends EventEmitter {
// Extend the ShellyComponent class prototype to include the Cover methods dynamically
if (isCoverComponent(this)) {
this.Open = function () {
- // this.setValue('state', 'open');
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { go: 'open' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Open`, { id: this.index });
};
this.Close = function () {
- // this.setValue('state', 'close');
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { go: 'close' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Close`, { id: this.index });
};
this.Stop = function () {
- // this.setValue('state', 'stop');
if (device.gen === 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${id.slice(0, id.indexOf(':'))}/${this.index}`, { go: 'stop' });
if (device.gen !== 1) ShellyDevice.fetch(device.shelly, device.log, device.host, `${this.name}.Stop`, { id: this.index });
};
@@ -190,6 +183,14 @@ export class ShellyComponent extends EventEmitter {
}
}
+ override emit(eventName: K, ...args: ShellyComponentEvent[K]): boolean {
+ return super.emit(eventName, ...args);
+ }
+
+ override on(eventName: K, listener: (...args: ShellyComponentEvent[K]) => void): this {
+ return super.on(eventName, listener);
+ }
+
/**
* Checks if the component has a property with the specified key.
*
diff --git a/src/shellyDevice.real.test.ts b/src/shellyDevice.real.test.ts
index 2049550..216974a 100644
--- a/src/shellyDevice.real.test.ts
+++ b/src/shellyDevice.real.test.ts
@@ -15,7 +15,7 @@ describe('Shellies', () => {
let device: ShellyDevice | undefined;
const firmwareGen1 = 'v1.14.0-gcb84623';
- const firmwareGen2 = '1.4.2-gc2639da';
+ const firmwareGen2 = '1.4.4-g6d2a586';
const address = '30:f6:ef:69:2b:c5';
beforeAll(async () => {
diff --git a/src/shellyDevice.realgen1.test.ts b/src/shellyDevice.realgen1.test.ts
index 8ffca06..c4e0a22 100644
--- a/src/shellyDevice.realgen1.test.ts
+++ b/src/shellyDevice.realgen1.test.ts
@@ -23,8 +23,10 @@ describe('Shellies', () => {
//
});
// consoleLogSpy.mockRestore();
+ shelly.dataPath = 'temp';
shelly.setLogLevel(LogLevel.DEBUG, true, true, true);
shelly.startCoap(0);
+ shelly.startMdns(0, '192.168.1.189', 'udp4', true);
await wait(2000);
});
@@ -51,36 +53,35 @@ describe('Shellies', () => {
expect(shelly).toBeDefined();
});
- describe('test real gen 1 shelly1-34945472A643 240', () => {
+ test('Create a gen 1 shelly1 device and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shelly1 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.240');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.240');
- expect(device.mac).toBe('34945472A643');
- expect(device.profile).toBe(undefined);
- expect(device.model).toBe('SHSW-1');
- expect(device.id).toBe('shelly1-34945472A643');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('relay:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isSwitchComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.240');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.240');
+ expect(device.mac).toBe('34945472A643');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHSW-1');
+ expect(device.id).toBe('shelly1-34945472A643');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('1 Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('relay:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -94,41 +95,39 @@ describe('Shellies', () => {
await waiter('Off', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 20000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
- describe('test real gen 1 shelly1l-E8DB84AAD781 241', () => {
+ test('Create a gen 1 shelly1l device and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shelly1 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.241');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.241');
- expect(device.mac).toBe('E8DB84AAD781');
- expect(device.profile).toBe(undefined);
- expect(device.model).toBe('SHSW-L');
- expect(device.id).toBe('shelly1l-E8DB84AAD781');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('relay:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isSwitchComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.241');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.241');
+ expect(device.mac).toBe('E8DB84AAD781');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHSW-L');
+ expect(device.id).toBe('shelly1l-E8DB84AAD781');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('1L Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('relay:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -142,41 +141,39 @@ describe('Shellies', () => {
await waiter('Off', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 20000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
- describe('test real gen 1 shellydimmer2 119 with auth', () => {
+ test('Create a gen 1 shellydimmer2 device and update', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellydimmer2 device and update', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.219');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.219');
- expect(device.mac).toBe('98CDAC0D01BB');
- expect(device.profile).toBe(undefined);
- expect(device.model).toBe('SHDM-2');
- expect(device.id).toBe('shellydimmer2-98CDAC0D01BB');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(true);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('light:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isLightComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.219');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.219');
+ expect(device.mac).toBe('98CDAC0D01BB');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHDM-2');
+ expect(device.id).toBe('shellydimmer2-98CDAC0D01BB');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(true);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('Dimmer2 Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -199,41 +196,39 @@ describe('Shellies', () => {
await waiter('Off', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 20000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
- describe('test real gen 1 shellybulbduo-34945479CFA4 154', () => {
+ test('Create a gen 1 shellybulbduo device and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellybulbduo device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.154');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.154');
- expect(device.mac).toBe('34945479CFA4');
- expect(device.profile).toBe(undefined);
- expect(device.model).toBe('SHBDUO-1');
- expect(device.id).toBe('shellybulbduo-34945479CFA4');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('light:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isLightComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.154');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.154');
+ expect(device.mac).toBe('34945479CFA4');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHBDUO-1');
+ expect(device.id).toBe('shellybulbduo-34945479CFA4');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('Duo Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -250,41 +245,39 @@ describe('Shellies', () => {
await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 30000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
- describe('test real gen 1 shellycolorbulb-485519EE12A7 155', () => {
+ test('Create a gen 1 shellycolorbulb device and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellybulbduo device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.155');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.155');
- expect(device.mac).toBe('485519EE12A7');
- expect(device.profile).toBe('color');
- expect(device.model).toBe('SHCB-1');
- expect(device.id).toBe('shellycolorbulb-485519EE12A7');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('light:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isLightComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.155');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.155');
+ expect(device.mac).toBe('485519EE12A7');
+ expect(device.profile).toBe('color');
+ expect(device.model).toBe('SHCB-1');
+ expect(device.id).toBe('shellycolorbulb-485519EE12A7');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('Bulb Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -307,43 +300,41 @@ describe('Shellies', () => {
await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 30000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
- describe('test real gen 1 shellyrgbw2-EC64C9D3FFEF mode color 226', () => {
+ test('Create a gen 1 shellyrgbw2 device color mode and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellyrgbw2 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.226');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.226');
- expect(device.mac).toBe('EC64C9D3FFEF');
- expect(device.profile).toBe('color');
- expect(device.model).toBe('SHRGBW2');
- expect(device.id).toBe('shellyrgbw2-EC64C9D3FFEF');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
-
- const component = device.getComponent('light:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isLightComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.226');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.226');
+ expect(device.mac).toBe('EC64C9D3FFEF');
+ expect(device.profile).toBe('color');
+ expect(device.model).toBe('SHRGBW2');
+ expect(device.id).toBe('shellyrgbw2-EC64C9D3FFEF');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('RGBW2 Gen1 Color');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -360,7 +351,7 @@ describe('Shellies', () => {
await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
component.Level(60);
- await waiter('Level(60)', () => { return component.getValue('brightness') === 60; }, true);
+ await waiter('Level(60)', () => { return component.getValue('gain') === 60; }, true);
component.ColorRGB(0, 255, 0);
await waiter('ColorRGB(0, 255, 0)', () => { return component.getValue('red') === 0 && component.getValue('green') === 255 && component.getValue('blue') === 0; }, true);
@@ -369,67 +360,65 @@ describe('Shellies', () => {
await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 30000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
- describe('test real gen 1 shellyrgbw2-EC64C9D199AD mode white 152', () => {
+ test('Create a gen 1 shellyrgbw2 device white mode and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellyrgbw2 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.152');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.152');
- expect(device.mac).toBe('EC64C9D199AD');
- expect(device.profile).toBe('white');
- expect(device.model).toBe('SHRGBW2');
- expect(device.id).toBe('shellyrgbw2-EC64C9D199AD');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
- expect(device.getComponentIds()).toStrictEqual([
- 'wifi_ap',
- 'wifi_sta',
- 'wifi_sta1',
- 'mqtt',
- 'coiot',
- 'sntp',
- 'cloud',
- 'light:0',
- 'light:1',
- 'light:2',
- 'light:3',
- 'sys',
- 'meter:0',
- 'meter:1',
- 'meter:2',
- 'meter:3',
- 'input:0',
- ]);
-
- const component = device.getComponent('light:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isLightComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.152');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.152');
+ expect(device.mac).toBe('EC64C9D199AD');
+ expect(device.profile).toBe('white');
+ expect(device.model).toBe('SHRGBW2');
+ expect(device.id).toBe('shellyrgbw2-EC64C9D199AD');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('RGBW2 Gen1 White');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
+ expect(device.getComponentIds()).toStrictEqual([
+ 'wifi_ap',
+ 'wifi_sta',
+ 'wifi_sta1',
+ 'mqtt',
+ 'coiot',
+ 'sntp',
+ 'cloud',
+ 'light:0',
+ 'light:1',
+ 'light:2',
+ 'light:3',
+ 'sys',
+ 'meter:0',
+ 'meter:1',
+ 'meter:2',
+ 'meter:3',
+ 'input:0',
+ ]);
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
- component.Level(40);
- await waiter('Level(40)', () => { return component.getValue('brightness') === 40; }, true);
+ component.Level(30);
+ await waiter('Level(30)', () => { return component.getValue('brightness') === 30; }, true);
component.Off();
await waiter('Off', () => { return component.getValue('state') === false; }, true);
@@ -444,41 +433,39 @@ describe('Shellies', () => {
await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 30000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
- describe('test real gen 1 shellyem3-485519D732F4 249', () => {
+ test('Create a gen 1 shellyem3 device and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shellyem3 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.249');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.249');
- expect(device.mac).toBe('485519D732F4');
- expect(device.profile).toBe(undefined);
- expect(device.model).toBe('SHEM-3');
- expect(device.id).toBe('shellyem3-485519D732F4');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- const component = device.getComponent('relay:0');
- expect(component).not.toBeUndefined();
-
- // prettier-ignore
- if (isSwitchComponent(component)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.249');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.249');
+ expect(device.mac).toBe('485519D732F4');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHEM-3');
+ expect(device.id).toBe('shellyem3-485519D732F4');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('3EM Gen1');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('relay:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -492,59 +479,57 @@ describe('Shellies', () => {
await waiter('Off', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 20000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
- describe('test real gen 1 shellyswitch25-3494547BF36C 236', () => {
+ test('Create a gen 1 shellyswitch25 device mode relay and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shelly1 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.236');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.236');
- expect(device.mac).toBe('3494547BF36C');
- expect(device.profile).toBe('switch');
- expect(device.model).toBe('SHSW-25');
- expect(device.id).toBe('shellyswitch25-3494547BF36C');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Relay', 'PowerMeter', 'Input', 'Sys']);
- expect(device.getComponentIds()).toStrictEqual([
- 'wifi_ap',
- 'wifi_sta',
- 'wifi_sta1',
- 'mqtt',
- 'coiot',
- 'sntp',
- 'cloud',
- 'relay:0',
- 'relay:1',
- 'meter:0',
- 'meter:1',
- 'input:0',
- 'input:1',
- 'sys',
- ]);
-
- const component0 = device.getComponent('relay:0');
- expect(component0).not.toBeUndefined();
-
- // prettier-ignore
- if (isSwitchComponent(component0)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.236');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.236');
+ expect(device.mac).toBe('3494547BF36C');
+ expect(device.profile).toBe('switch');
+ expect(device.model).toBe('SHSW-25');
+ expect(device.id).toBe('shellyswitch25-3494547BF36C');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('2.5 Gen1 Relay');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Relay', 'PowerMeter', 'Input', 'Sys']);
+ expect(device.getComponentIds()).toStrictEqual([
+ 'wifi_ap',
+ 'wifi_sta',
+ 'wifi_sta1',
+ 'mqtt',
+ 'coiot',
+ 'sntp',
+ 'cloud',
+ 'relay:0',
+ 'relay:1',
+ 'meter:0',
+ 'meter:1',
+ 'input:0',
+ 'input:1',
+ 'sys',
+ ]);
+
+ const component0 = device.getComponent('relay:0');
+ expect(component0).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component0)) {
component0.On();
await waiter('On', () => { return component0.getValue('state') === true; }, true);
@@ -558,11 +543,11 @@ describe('Shellies', () => {
await waiter('Off', () => { return component0.getValue('state') === false; }, true);
}
- const component1 = device.getComponent('relay:1');
- expect(component1).not.toBeUndefined();
+ const component1 = device.getComponent('relay:1');
+ expect(component1).not.toBeUndefined();
- // prettier-ignore
- if (isSwitchComponent(component1)) {
+ // prettier-ignore
+ if (isSwitchComponent(component1)) {
component1.On();
await waiter('On', () => { return component1.getValue('state') === true; }, true);
@@ -576,44 +561,42 @@ describe('Shellies', () => {
await waiter('Off', () => { return component1.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 20000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
- describe('test real gen 1 shellyswitch25-3494546BBF7E 222', () => {
+ test('Create a gen 1 shellyswitch25 device mode roller and send commands', async () => {
if (getMacAddress() !== address) return;
-
- test('Create a gen 1 shelly1 device and send commands', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.222');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
-
- expect(device.host).toBe('192.168.1.222');
- expect(device.mac).toBe('3494546BBF7E');
- expect(device.profile).toBe('cover');
- expect(device.model).toBe('SHSW-25');
- expect(device.id).toBe('shellyswitch25-3494546BBF7E');
- expect(device.firmware).toBe(firmwareGen1);
- expect(device.auth).toBe(false);
- expect(device.gen).toBe(1);
- expect(device.hasUpdate).toBe(false);
- expect(device.username).toBe('admin');
- expect(device.password).toBe('tango');
-
- await device.fetchUpdate();
-
- await device.saveDevicePayloads('temp');
-
- expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Roller', 'PowerMeter', 'Input', 'Sys']);
- expect(device.getComponentIds()).toStrictEqual(['wifi_ap', 'wifi_sta', 'wifi_sta1', 'mqtt', 'coiot', 'sntp', 'cloud', 'roller:0', 'meter:0', 'input:0', 'input:1', 'sys']);
-
- const component0 = device.getComponent('roller:0');
- expect(component0).not.toBeUndefined();
-
- // prettier-ignore
- if (isCoverComponent(component0)) {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.222');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.222');
+ expect(device.mac).toBe('3494546BBF7E');
+ expect(device.profile).toBe('cover');
+ expect(device.model).toBe('SHSW-25');
+ expect(device.id).toBe('shellyswitch25-3494546BBF7E');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('2.5 Gen1 Roller');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Roller', 'PowerMeter', 'Input', 'Sys']);
+ expect(device.getComponentIds()).toStrictEqual(['wifi_ap', 'wifi_sta', 'wifi_sta1', 'mqtt', 'coiot', 'sntp', 'cloud', 'roller:0', 'meter:0', 'input:0', 'input:1', 'sys']);
+
+ const component0 = device.getComponent('roller:0');
+ expect(component0).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isCoverComponent(component0)) {
component0.Open();
await waiter('Open', () => { return component0.getValue('state') === 'stop'; }, true, 30000);
await waiter('Open', () => { return component0.getValue('current_pos') === 100; }, true, 30000);
@@ -631,8 +614,7 @@ describe('Shellies', () => {
await waiter('Open', () => { return component0.getValue('current_pos') === 100; }, true, 30000);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 60000);
- });
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 60000);
});
diff --git a/src/shellyDevice.realgen3.test.ts b/src/shellyDevice.realgen3.test.ts
index 5df0772..b188deb 100644
--- a/src/shellyDevice.realgen3.test.ts
+++ b/src/shellyDevice.realgen3.test.ts
@@ -6,6 +6,7 @@ import { AnsiLogger, LogLevel, TimestampFormat } from 'matterbridge/logger';
import { getMacAddress, wait, waiter } from 'matterbridge/utils';
import { jest } from '@jest/globals';
import { isCoverComponent, isLightComponent, isSwitchComponent, ShellyCoverComponent, ShellySwitchComponent } from './shellyComponent.js';
+import { ShellyData } from './shellyTypes.js';
describe('Shellies', () => {
let consoleLogSpy: jest.SpiedFunction;
@@ -14,8 +15,7 @@ describe('Shellies', () => {
const shelly = new Shelly(log, 'admin', 'tango');
let device: ShellyDevice | undefined;
- const firmwareGen1 = 'v1.14.0-gcb84623';
- const firmwareGen2 = '1.4.2-gc2639da';
+ const firmwareGen2 = '1.4.4-g6d2a586';
const address = '30:f6:ef:69:2b:c5';
beforeAll(async () => {
@@ -23,7 +23,9 @@ describe('Shellies', () => {
//
});
// consoleLogSpy.mockRestore();
+ shelly.dataPath = 'temp';
shelly.setLogLevel(LogLevel.DEBUG, true, true, true);
+ shelly.startMdns(0, '192.168.1.189', 'udp4', true);
await wait(2000);
});
@@ -50,34 +52,95 @@ describe('Shellies', () => {
expect(shelly).toBeDefined();
});
- describe('test real gen 3 shelly1minig3 221 with auth', () => {
+ test('create a sleepy gen 3 shellyhtg3 device and update', async () => {
if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.100');
+ // expect(device).not.toBeUndefined(); // Skip this test if is sleeping
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
- test('create a gen 3 shelly1minig3 device and update', async () => {
- device = await ShellyDevice.create(shelly, log, '192.168.1.221');
- expect(device).not.toBeUndefined();
- if (!device) return;
- shelly.addDevice(device);
- (device as any).wsClient?.start();
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.100');
+ expect(device.model).toBe('S3SN-0U12A');
+ expect(device.mac).toBe('3030F9EC8468');
+ expect(device.id).toBe('shellyhtg3-3030F9EC8468');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe('1.4.5-gbf870ca'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('H&T Gen3');
- expect(device.gen).toBe(3);
- expect(device.host).toBe('192.168.1.221');
- expect(device.model).toBe('S3SW-001X8EU');
- expect(device.mac).toBe('543204547478');
- expect(device.id).toBe('shelly1minig3-543204547478');
- expect(device.hasUpdate).toBe(false);
- expect(device.firmware).toBe(firmwareGen2);
- expect(device.auth).toBe(true);
+ await device.fetchUpdate();
- await device.fetchUpdate();
+ await device.saveDevicePayloads('temp');
- await device.saveDevicePayloads('temp');
+ expect(device.components.length).toBe(12);
+ // prettier-ignore
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Devicepower', 'Humidity', 'MQTT', 'Sys', 'Sntp', 'Temperature', 'WiFi', 'WS']);
+ // prettier-ignore
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'devicepower:0', 'humidity:0', 'mqtt', 'sys', 'sntp', 'temperature:0', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
- const component = device.getComponent('switch:0');
- expect(component).not.toBeUndefined();
+ expect(device.getComponent('devicepower:0')).not.toBeUndefined();
+ expect(device.getComponent('devicepower:0')?.getValue('battery')).toBeDefined();
+ const battery = device.getComponent('devicepower:0')?.getValue('battery') as ShellyData;
+ expect(battery).toBeDefined();
+ expect(battery.V).toBeGreaterThanOrEqual(1);
+ expect(battery.percent).toBeGreaterThanOrEqual(1);
+ expect(device.getComponent('temperature:0')).not.toBeUndefined();
+ expect(device.getComponent('temperature:0')?.getValue('tC')).toBeGreaterThanOrEqual(1);
+ expect(device.getComponent('temperature:0')?.getValue('tF')).toBeGreaterThanOrEqual(1);
+ expect(device.getComponent('humidity:0')).not.toBeUndefined();
+ expect(device.getComponent('humidity:0')?.getValue('rh')).toBeGreaterThanOrEqual(1);
- // prettier-ignore
- if (isSwitchComponent(component)) {
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shelly1g3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.157');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.157');
+ expect(device.model).toBe('S3SW-001X16EU');
+ expect(device.mac).toBe('34B7DACAC830');
+ expect(device.id).toBe('shelly1g3-34B7DACAC830');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('1 Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(11);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
component.On();
await waiter('On', () => { return component.getValue('state') === true; }, true);
@@ -91,8 +154,952 @@ describe('Shellies', () => {
await waiter('Off', () => { return component.getValue('state') === false; }, true);
}
- shelly.removeDevice(device);
- device.destroy();
- }, 30000);
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shelly1minig3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.221');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.221');
+ expect(device.model).toBe('S3SW-001X8EU');
+ expect(device.mac).toBe('543204547478');
+ expect(device.id).toBe('shelly1minig3-543204547478');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(true);
+ expect(device.name).toBe('1mini Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(11);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shelly1pmg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.158');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.158');
+ expect(device.model).toBe('S3SW-001P16EU');
+ expect(device.mac).toBe('34B7DAC68344');
+ expect(device.id).toBe('shelly1pmg3-34B7DAC68344');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('1PM Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(11);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(3);
+ expect(device.bthomeDevices.has('7c:c6:b6:65:2d:87')).toBe(true);
+ expect(device.bthomeDevices.get('7c:c6:b6:65:2d:87')?.model).toBe('Shelly BLU HT');
+ expect(device.bthomeDevices.has('0c:ef:f6:f1:d7:7b')).toBe(true);
+ expect(device.bthomeDevices.get('0c:ef:f6:f1:d7:7b')?.model).toBe('Shelly BLU DoorWindow');
+ expect(device.bthomeDevices.has('38:39:8f:8b:d2:29')).toBe(true);
+ expect(device.bthomeDevices.get('38:39:8f:8b:d2:29')?.model).toBe('Shelly BLU Button1');
+
+ expect(device.bthomeSensors.size).toBe(10);
+ // BLU HT
+ expect(device.bthomeSensors.has('bthomesensor:200')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:201')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.name).toBe('Humidity');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorId).toBe(46);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:202')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:203')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorIdx).toBe(0);
+
+ // BLU DoorWindow
+ expect(device.bthomeSensors.has('bthomesensor:204')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:205')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.name).toBe('Illuminance');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorId).toBe(5);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:206')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.name).toBe('Contact');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorId).toBe(45);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:207')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.name).toBe('Rotation');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorId).toBe(63);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorIdx).toBe(0);
+
+ // BLU Button
+ expect(device.bthomeSensors.has('bthomesensor:208')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.addr).toBe('38:39:8f:8b:d2:29');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:209')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.addr).toBe('38:39:8f:8b:d2:29');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:210')).toBe(false);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shelly1pmminig3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.225');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.225');
+ expect(device.model).toBe('S3SW-001P8EU');
+ expect(device.mac).toBe('543204519264');
+ expect(device.id).toBe('shelly1pmminig3-543204519264');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('1PMmini Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(11);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shelly2pmg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.166');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.166');
+ expect(device.model).toBe('S3SW-002P16EU');
+ expect(device.mac).toBe('34CDB0770C4C');
+ expect(device.id).toBe('shelly2pmg3-34CDB0770C4C');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe('1.4.99-2pmg3prod0-ge3db05c'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('2PM Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(13);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'input:1', 'mqtt', 'switch:0', 'switch:1', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeDevices.size).toBe(1);
+ expect(device.bthomeDevices.has('7c:c6:b6:58:b9:a0')).toBe(true);
+ expect(device.bthomeDevices.get('7c:c6:b6:58:b9:a0')?.model).toBe('Shelly BLU RC Button 4');
+
+ expect(device.bthomeSensors.size).toBe(5);
+ expect(device.bthomeSensors.has('bthomesensor:200')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:201')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:202')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorIdx).toBe(1);
+
+ expect(device.bthomeSensors.has('bthomesensor:203')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorIdx).toBe(2);
+
+ expect(device.bthomeSensors.has('bthomesensor:204')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorIdx).toBe(3);
+ expect(device.bthomeTrvs.size).toBe(0);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+ expect(component?.getValue('voltage')).toBeGreaterThan(200);
+ expect(component?.hasProperty('apower')).toBe(true);
+ expect(component?.hasProperty('current')).toBe(true);
+ expect(component?.hasProperty('aenergy')).toBe(true);
+ expect(component?.hasProperty('freq')).toBe(true);
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ const component1 = device.getComponent('switch:1');
+ expect(component1).not.toBeUndefined();
+ expect(component1?.getValue('voltage')).toBeGreaterThan(200);
+ expect(component1?.hasProperty('apower')).toBe(true);
+ expect(component1?.hasProperty('current')).toBe(true);
+ expect(component1?.hasProperty('aenergy')).toBe(true);
+ expect(component1?.hasProperty('freq')).toBe(true);
+
+ // prettier-ignore
+ if (isSwitchComponent(component1)) {
+ component1.On();
+ await waiter('On', () => { return component1.getValue('state') === true; }, true);
+
+ component1.Off();
+ await waiter('Off', () => { return component1.getValue('state') === false; }, true);
+
+ component1.Toggle();
+ await waiter('Toggle', () => { return component1.getValue('state') === true; }, true);
+
+ component1.Off();
+ await waiter('Off', () => { return component1.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('create a gen 3 shellyblugwg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ // consoleLogSpy.mockRestore();
+
+ device = await ShellyDevice.create(shelly, log, '192.168.1.164');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.164');
+ expect(device.model).toBe('S3GW-1DBT001');
+ expect(device.mac).toBe('34CDB077BCD4');
+ expect(device.id).toBe('shellyblugwg3-34CDB077BCD4');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe('1.4.99-blugwg3prod2-g689f175'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('BLU Gateway Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(10);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Blugw', 'Cloud', 'MQTT', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'blugw', 'cloud', 'mqtt', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(2);
+ expect(device.bthomeTrvs.has('28:68:47:fc:9a:6b')).toBe(true);
+ expect(device.bthomeTrvs.get('28:68:47:fc:9a:6b')?.id).toBe(200);
+ expect(device.bthomeTrvs.get('28:68:47:fc:9a:6b')?.bthomedevice).toBe('bthomedevice:200');
+ expect(device.bthomeTrvs.has('28:db:a7:b5:d1:ca')).toBe(true);
+ expect(device.bthomeTrvs.get('28:db:a7:b5:d1:ca')?.id).toBe(201);
+ expect(device.bthomeTrvs.get('28:db:a7:b5:d1:ca')?.bthomedevice).toBe('bthomedevice:203');
+
+ expect(device.bthomeDevices.size).toBe(5);
+ expect(device.bthomeDevices.has('28:68:47:fc:9a:6b')).toBe(true);
+ expect(device.bthomeDevices.get('28:68:47:fc:9a:6b')?.model).toBe('Shelly BLU Trv');
+ expect(device.bthomeDevices.get('28:68:47:fc:9a:6b')?.id).toBe(200);
+ expect(device.bthomeDevices.get('28:68:47:fc:9a:6b')?.blutrv_id).toBe(200);
+ expect(device.bthomeDevices.has('28:db:a7:b5:d1:ca')).toBe(true);
+ expect(device.bthomeDevices.get('28:db:a7:b5:d1:ca')?.model).toBe('Shelly BLU Trv');
+ expect(device.bthomeDevices.get('28:db:a7:b5:d1:ca')?.id).toBe(203);
+ expect(device.bthomeDevices.get('28:db:a7:b5:d1:ca')?.blutrv_id).toBe(201);
+
+ // TRV 200
+ expect(device.bthomeSensors.size).toBe(20);
+ expect(device.bthomeSensors.has('bthomesensor:200')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.addr).toBe('28:68:47:fc:9a:6b');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:201')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.addr).toBe('28:68:47:fc:9a:6b');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:202')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.addr).toBe('28:68:47:fc:9a:6b');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:203')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.addr).toBe('28:68:47:fc:9a:6b');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorIdx).toBe(1);
+
+ // BLU HT
+ expect(device.bthomeSensors.has('bthomesensor:204')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:205')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.name).toBe('Humidity');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorId).toBe(46);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:206')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:207')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.addr).toBe('7c:c6:b6:65:2d:87');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorIdx).toBe(0);
+
+ // BLU DoorWindow
+ expect(device.bthomeSensors.has('bthomesensor:208')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:209')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.name).toBe('Illuminance');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorId).toBe(5);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:210')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:210')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:210')?.name).toBe('Contact');
+ expect(device.bthomeSensors.get('bthomesensor:210')?.sensorId).toBe(45);
+ expect(device.bthomeSensors.get('bthomesensor:210')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:211')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:211')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:211')?.name).toBe('Rotation');
+ expect(device.bthomeSensors.get('bthomesensor:211')?.sensorId).toBe(63);
+ expect(device.bthomeSensors.get('bthomesensor:211')?.sensorIdx).toBe(0);
+
+ // TRV 201
+ expect(device.bthomeSensors.has('bthomesensor:212')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:212')?.addr).toBe('28:db:a7:b5:d1:ca');
+ expect(device.bthomeSensors.get('bthomesensor:212')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:212')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:212')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:213')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:213')?.addr).toBe('28:db:a7:b5:d1:ca');
+ expect(device.bthomeSensors.get('bthomesensor:213')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:213')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:213')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:214')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:214')?.addr).toBe('28:db:a7:b5:d1:ca');
+ expect(device.bthomeSensors.get('bthomesensor:214')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:214')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:214')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:215')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:215')?.addr).toBe('28:db:a7:b5:d1:ca');
+ expect(device.bthomeSensors.get('bthomesensor:215')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:215')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:215')?.sensorIdx).toBe(1);
+
+ // BLU HT
+ expect(device.bthomeSensors.has('bthomesensor:216')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:216')?.addr).toBe('7c:c6:b6:bd:7a:9a');
+ expect(device.bthomeSensors.get('bthomesensor:216')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:216')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:216')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:217')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:217')?.addr).toBe('7c:c6:b6:bd:7a:9a');
+ expect(device.bthomeSensors.get('bthomesensor:217')?.name).toBe('Humidity');
+ expect(device.bthomeSensors.get('bthomesensor:217')?.sensorId).toBe(46);
+ expect(device.bthomeSensors.get('bthomesensor:217')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:218')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:218')?.addr).toBe('7c:c6:b6:bd:7a:9a');
+ expect(device.bthomeSensors.get('bthomesensor:218')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:218')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:218')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:219')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:219')?.addr).toBe('7c:c6:b6:bd:7a:9a');
+ expect(device.bthomeSensors.get('bthomesensor:219')?.name).toBe('Temperature');
+ expect(device.bthomeSensors.get('bthomesensor:219')?.sensorId).toBe(69);
+ expect(device.bthomeSensors.get('bthomesensor:219')?.sensorIdx).toBe(0);
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('Create a gen 3 shellyddimmerg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.242');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.host).toBe('192.168.1.242');
+ expect(device.mac).toBe('84FCE636832C');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('S3DM-0A1WW');
+ expect(device.id).toBe('shellyddimmerg3-84FCE636832C');
+ expect(device.firmware).toBe('g55db545'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(3);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('DALI Dimmer Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(12);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'Light', 'MQTT', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'input:1', 'light:0', 'mqtt', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(100);
+ await waiter('Level(100)', () => { return component.getValue('brightness') === 100; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Level(50);
+ await waiter('Level(50)', () => { return component.getValue('brightness') === 50; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(1);
+ await waiter('Level(1)', () => { return component.getValue('brightness') === 1; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('Create a gen 3 shellyemg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.243');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.host).toBe('192.168.1.243');
+ expect(device.mac).toBe('84FCE636582C');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('S3EM-002CXCEU');
+ expect(device.id).toBe('shellyemg3-84FCE636582C');
+ expect(device.firmware).toBe('g1216eb0'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(3);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('EM Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(12);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'PowerMeter', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'em1:0', 'em1:1', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+
+ expect(device.bthomeDevices.size).toBe(4);
+
+ expect(device.bthomeDevices.has('0c:ef:f6:f1:d7:7b')).toBe(true);
+ expect(device.bthomeDevices.get('0c:ef:f6:f1:d7:7b')?.model).toBe('Shelly BLU DoorWindow');
+ expect(device.bthomeDevices.get('0c:ef:f6:f1:d7:7b')?.id).toBe(200);
+ expect(device.bthomeDevices.get('0c:ef:f6:f1:d7:7b')?.blutrv_id).toBe(0);
+
+ expect(device.bthomeDevices.has('0c:ae:5f:5a:0b:fa')).toBe(true);
+ expect(device.bthomeDevices.get('0c:ae:5f:5a:0b:fa')?.model).toBe('Shelly BLU Motion');
+ expect(device.bthomeDevices.get('0c:ae:5f:5a:0b:fa')?.id).toBe(201);
+ expect(device.bthomeDevices.get('0c:ae:5f:5a:0b:fa')?.blutrv_id).toBe(0);
+
+ expect(device.bthomeDevices.has('0c:ef:f6:01:8d:b8')).toBe(true);
+ expect(device.bthomeDevices.get('0c:ef:f6:01:8d:b8')?.model).toBe('Shelly BLU Wall Switch 4');
+ expect(device.bthomeDevices.get('0c:ef:f6:01:8d:b8')?.id).toBe(202);
+ expect(device.bthomeDevices.get('0c:ef:f6:01:8d:b8')?.blutrv_id).toBe(0);
+
+ expect(device.bthomeDevices.has('7c:c6:b6:58:b9:a0')).toBe(true);
+ expect(device.bthomeDevices.get('7c:c6:b6:58:b9:a0')?.model).toBe('Shelly BLU RC Button 4');
+ expect(device.bthomeDevices.get('7c:c6:b6:58:b9:a0')?.id).toBe(203);
+ expect(device.bthomeDevices.get('7c:c6:b6:58:b9:a0')?.blutrv_id).toBe(0);
+
+ expect(device.bthomeSensors.size).toBe(18);
+ // BLU DoorWindow
+ expect(device.bthomeSensors.has('bthomesensor:200')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:200')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:201')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.name).toBe('Illuminance');
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorId).toBe(5);
+ expect(device.bthomeSensors.get('bthomesensor:201')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:202')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.name).toBe('Contact');
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorId).toBe(45);
+ expect(device.bthomeSensors.get('bthomesensor:202')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:203')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.addr).toBe('0c:ef:f6:f1:d7:7b');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.name).toBe('Rotation');
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorId).toBe(63);
+ expect(device.bthomeSensors.get('bthomesensor:203')?.sensorIdx).toBe(0);
+
+ // BLU Motion
+ expect(device.bthomeSensors.has('bthomesensor:204')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.addr).toBe('0c:ae:5f:5a:0b:fa');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:204')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:205')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.addr).toBe('0c:ae:5f:5a:0b:fa');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.name).toBe('Illuminance');
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorId).toBe(5);
+ expect(device.bthomeSensors.get('bthomesensor:205')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:206')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.addr).toBe('0c:ae:5f:5a:0b:fa');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.name).toBe('Motion');
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorId).toBe(33);
+ expect(device.bthomeSensors.get('bthomesensor:206')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:207')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.addr).toBe('0c:ae:5f:5a:0b:fa');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:207')?.sensorIdx).toBe(0);
+
+ // BLU RC Wall Switch 4
+ expect(device.bthomeSensors.has('bthomesensor:208')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.addr).toBe('0c:ef:f6:01:8d:b8');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:208')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:209')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.addr).toBe('0c:ef:f6:01:8d:b8');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:209')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:210')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:210')?.addr).toBe('0c:ef:f6:01:8d:b8');
+ expect(device.bthomeSensors.get('bthomesensor:210')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:210')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:210')?.sensorIdx).toBe(1);
+
+ expect(device.bthomeSensors.has('bthomesensor:211')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:211')?.addr).toBe('0c:ef:f6:01:8d:b8');
+ expect(device.bthomeSensors.get('bthomesensor:211')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:211')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:211')?.sensorIdx).toBe(2);
+
+ expect(device.bthomeSensors.has('bthomesensor:212')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:212')?.addr).toBe('0c:ef:f6:01:8d:b8');
+ expect(device.bthomeSensors.get('bthomesensor:212')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:212')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:212')?.sensorIdx).toBe(3);
+
+ // BLU RC Button 4
+ expect(device.bthomeSensors.has('bthomesensor:213')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:213')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:213')?.name).toBe('Battery');
+ expect(device.bthomeSensors.get('bthomesensor:213')?.sensorId).toBe(1);
+ expect(device.bthomeSensors.get('bthomesensor:213')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:214')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:214')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:214')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:214')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:214')?.sensorIdx).toBe(0);
+
+ expect(device.bthomeSensors.has('bthomesensor:215')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:215')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:215')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:215')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:215')?.sensorIdx).toBe(1);
+
+ expect(device.bthomeSensors.has('bthomesensor:216')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:216')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:216')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:216')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:216')?.sensorIdx).toBe(2);
+
+ expect(device.bthomeSensors.has('bthomesensor:217')).toBe(true);
+ expect(device.bthomeSensors.get('bthomesensor:217')?.addr).toBe('7c:c6:b6:58:b9:a0');
+ expect(device.bthomeSensors.get('bthomesensor:217')?.name).toBe('Button');
+ expect(device.bthomeSensors.get('bthomesensor:217')?.sensorId).toBe(58);
+ expect(device.bthomeSensors.get('bthomesensor:217')?.sensorIdx).toBe(3);
+
+ expect(device.bthomeSensors.has('bthomesensor:218')).toBe(false);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ test('Create a gen 3 shellyi4g3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.159');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.host).toBe('192.168.1.159');
+ expect(device.mac).toBe('5432045661B4');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('S3SN-0024X');
+ expect(device.id).toBe('shellyi4g3-5432045661B4');
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(3);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+ expect(device.name).toBe('i4 Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(13);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'Input', 'MQTT', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'input:0', 'input:1', 'input:2', 'input:3', 'mqtt', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ expect(device.getComponent('input:0')).not.toBeUndefined();
+ expect(device.getComponent('input:0')?.getValue('enable')).toBe(true);
+ expect(device.getComponent('input:0')?.getValue('state')).toBe(null);
+ expect(device.getComponent('input:0')?.getValue('type')).toBe('button');
+
+ expect(device.getComponent('input:1')).not.toBeUndefined();
+ expect(device.getComponent('input:1')?.getValue('enable')).toBe(false);
+ expect(device.getComponent('input:1')?.getValue('state')).toBe(null);
+ expect(device.getComponent('input:1')?.getValue('type')).toBe('button');
+
+ expect(device.getComponent('input:2')).not.toBeUndefined();
+ expect(device.getComponent('input:2')?.getValue('enable')).toBe(true);
+ expect(device.getComponent('input:2')?.getValue('state')).toBe(false);
+ expect(device.getComponent('input:2')?.getValue('type')).toBe('switch');
+
+ expect(device.getComponent('input:3')).not.toBeUndefined();
+ expect(device.getComponent('input:3')?.getValue('enable')).toBe(false);
+ expect(device.getComponent('input:3')?.getValue('state')).toBe(null);
+ expect(device.getComponent('input:3')?.getValue('type')).toBe('switch');
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+
+ // eslint-disable-next-line jest/no-commented-out-tests
+ /*
+ test('create a gen 3 shellyplugsg3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.165');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.165');
+ expect(device.model).toBe('S3PL-00112EU');
+ expect(device.mac).toBe('5432045CE094');
+ expect(device.id).toBe('shellyplugsg3-5432045CE094');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe('1.2.3-plugsg3prod0-gec79607'); // firmwareGen2
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('Plug S Gen3 Matter');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(10);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'MQTT', 'Switch', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'mqtt', 'switch:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ // component.Off();
+ // await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ // component.Toggle();
+ // await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ // component.Off();
+ // await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
});
+ */
+
+ test('create a gen 3 shellypmminig3 device and update', async () => {
+ if (getMacAddress() !== address) return;
+ device = await ShellyDevice.create(shelly, log, '192.168.1.220');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+ expect((device as any).wsClient).not.toBeUndefined();
+ (device as any).wsClient?.start();
+
+ expect(device.gen).toBe(3);
+ expect(device.host).toBe('192.168.1.220');
+ expect(device.model).toBe('S3PM-001PCEU16');
+ expect(device.mac).toBe('84FCE63957F4');
+ expect(device.id).toBe('shellypmminig3-84FCE63957F4');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+ expect(device.name).toBe('PMmini Gen3');
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.components.length).toBe(10);
+ expect(device.getComponentNames()).toStrictEqual(['Ble', 'Cloud', 'MQTT', 'PowerMeter', 'Sys', 'Sntp', 'WiFi', 'WS']);
+ expect(device.getComponentIds()).toStrictEqual(['ble', 'cloud', 'mqtt', 'pm1:0', 'sys', 'sntp', 'wifi_ap', 'wifi_sta', 'wifi_sta1', 'ws']);
+
+ expect(device.bthomeTrvs.size).toBe(0);
+ expect(device.bthomeDevices.size).toBe(0);
+ expect(device.bthomeSensors.size).toBe(0);
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
});
diff --git a/src/shellyDevice.ts b/src/shellyDevice.ts
index b52ebe9..eab957b 100644
--- a/src/shellyDevice.ts
+++ b/src/shellyDevice.ts
@@ -49,6 +49,7 @@ import { isCoverComponent, isLightComponent, isSwitchComponent, ShellyComponent
interface ShellyDeviceEvent {
online: [];
offline: [];
+ awake: [];
update: [id: string, key: string, value: ShellyDataType];
bthome_event: [event: string];
bthomedevice_update: [addr: string, rssi: number, packet_id: number, last_updated_ts: number];
@@ -777,6 +778,20 @@ export class ShellyDevice extends EventEmitter {
});
}
+ device.on('awake', async () => {
+ log.debug(`***Device ${hk}${device.id}${db} host ${zb}${device.host}${db} is awake`);
+ if (device.sleepMode) {
+ try {
+ const awaken = await ShellyDevice.create(shelly, log, host);
+ await awaken?.saveDevicePayloads(shelly.dataPath);
+ awaken?.destroy();
+ log.debug(`***Device ${hk}${device.id}${db} host ${zb}${device.host}${db} updated cache file`);
+ } catch (error) {
+ log.debug(`***Error saving device cache ${hk}${device.id}${db} host ${zb}${device.host}${db}: ${error instanceof Error ? error.message : error}`);
+ }
+ }
+ });
+
device.shellyPayload = shellyPayload;
device.statusPayload = statusPayload;
device.settingsPayload = settingsPayload;
@@ -822,7 +837,7 @@ export class ShellyDevice extends EventEmitter {
}
} else if (isValidObject(event) && isValidString(event.event) && isValidString(event.component)) {
this.log.debug(`Device ${hk}${this.id}${db} has event ${YELLOW}${event.event}${db} from component ${idn}${event.component}${rs}${db}${rk}`);
- this.getComponent(event.component)?.emit('event', event.component, event.event);
+ this.getComponent(event.component)?.emit('event', event.component, event.event, event);
} else {
this.log.debug(`*Unknown event:${rs}\n`, event);
}
@@ -984,8 +999,9 @@ export class ShellyDevice extends EventEmitter {
let index = 0;
for (const light of data[key] as ShellyData[]) {
const component = this.getComponent(`${key.slice(0, 5)}:${index++}`);
+ if (!component) this.log.debug(`***Component ${key.slice(0, 5)}:${index} not found`);
if (component && light.ison !== undefined) component.setValue('state', light.ison as boolean);
- if (component && light.gain !== undefined) component.setValue('brightness', light.gain as number);
+ if (component && light.gain !== undefined) component.setValue('brightness', light.gain as number); // gain is used by color channels and brightness by white channels
}
}
}
@@ -1070,10 +1086,10 @@ export class ShellyDevice extends EventEmitter {
btHomePayload = (await ShellyDevice.fetch(this.shelly, this.log, this.host, 'Shelly.GetComponents', { dynamic_only: true, offset })) as unknown as BTHomeComponentPayload;
if (btHomePayload && btHomePayload.components) {
btHomeComponents.push(...btHomePayload.components);
+ offset += btHomePayload.components.length;
}
- offset += btHomeComponents.length;
} while (btHomePayload && offset < btHomePayload.total);
- this.componentsPayload = { components: btHomeComponents, cfg_rev: btHomePayload.cfg_rev, offset: 0, total: btHomeComponents.length };
+ this.componentsPayload = { components: btHomeComponents, cfg_rev: btHomePayload.cfg_rev | 0, offset: 0, total: btHomeComponents.length };
}
if (this.cached) {
this.cached = false;
diff --git a/src/wsClient.ts b/src/wsClient.ts
index 7f9babd..1e20bfb 100644
--- a/src/wsClient.ts
+++ b/src/wsClient.ts
@@ -420,45 +420,3 @@ export class WsClient extends EventEmitter {
}
}
}
-
-/*
-// Start the WebSocket client with the following command: node dist/wsClient.js startWsClient
-if (process.argv.includes('startWsClient')) {
- WsClient.logLevel = LogLevel.DEBUG;
-
- const wsClient1 = new WsClient('shellypro1pm-EC6260927F7C', '192.168.1.151', 'tango');
- wsClient1.start();
-
- const wsClient2 = new WsClient('shellyprodm1pm-34987A4957C4', '192.168.1.156', 'tango');
- wsClient2.start();
-
- setTimeout(() => {
- wsClient1.sendRequest('Switch.Set', { id: 0, on: true });
- wsClient2.sendRequest('Light.Set', { id: 0, on: true });
- }, 5000);
-
- setTimeout(() => {
- wsClient2.sendRequest('Light.Set', { id: 0, on: false });
- }, 10000);
-
- setTimeout(() => {
- wsClient1.sendRequest('Shelly.GetComponents', {});
- wsClient2.sendRequest('Shelly.GetComponents', {});
- }, 15000);
-
- setTimeout(() => {
- wsClient1.sendRequest('Shelly.ListMethods', {});
- wsClient2.sendRequest('Shelly.ListMethods', {});
- }, 20000);
-
- setTimeout(() => {
- wsClient1.stop();
- wsClient2.stop();
- }, 30000);
-
- process.on('SIGINT', async function () {
- wsClient1.stop();
- wsClient2.stop();
- });
-}
-*/
diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json
deleted file mode 100644
index 3e1c3ab..0000000
--- a/tsconfig.eslint.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "incremental": true,
- "composite": true,
- "target": "esnext",
- "lib": ["ESNext"],
- "module": "nodenext",
- "rootDir": "./src",
- "moduleResolution": "nodenext",
- "resolveJsonModule": true,
- "allowJs": true,
- "checkJs": true,
- "declaration": true,
- "declarationMap": true,
- "sourceMap": true,
- "outDir": "./dist",
- "preserveConstEnums": true,
- "allowSyntheticDefaultImports": true,
- "esModuleInterop": true,
- "forceConsistentCasingInFileNames": true,
- "strict": true,
- "noImplicitAny": true,
- "alwaysStrict": true,
- "noImplicitOverride": true,
- "skipLibCheck": true
- }
-}