[matter] Upgrades to matter.js v0.14.0, adds matter error codes and translations (#18786)

Signed-off-by: Dan Cunningham <dan@digitaldan.com>
pull/18623/merge
Dan Cunningham 2025-06-10 14:04:16 -07:00 committed by GitHub
parent 9f73acaf39
commit c9eaa00145
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 395 additions and 151 deletions

View File

@ -2,6 +2,8 @@
The Matter Binding for openHAB allows seamless integration with Matter-compatible devices.
It currently supports version 1.4.1 of the Matter specification and earlier.
## Supported functionality
This binding supports two different types of Matter functionality which operate independently of each other.
@ -17,7 +19,7 @@ For more information on the Matter specification, see the [Matter Ecosystem Over
## Matter.JS Runtime
This binding uses the excellent [matter.js](https://github.com/project-chip/matter.js) implementation of the the Matter 1.4 protocol.
This binding uses the excellent [matter.js](https://github.com/project-chip/matter.js) implementation of the the Matter 1.4.1 protocol.
As such, this binding requires NodesJS 18+ and will attempt to download and cache an appropriate version when started if a version is not already installed on the system.
Alpine Linux users (typically docker) and those on older Linux distributions will need to install this manually as the official NodeJS versions are not compatible.
@ -38,7 +40,7 @@ This describes the Matter controller functionality for discovering and controlli
The Matter Binding supports the following types of things:
- `controller`: The main controller that interfaces with Matter devices.
It requires the configuration parameter `nodeId` which sets the local Matter node ID for this controller (must be unique in the fabric).
It requires the configuration parameter `nodeId` which sets the local Matter node ID for this controller (must be unique in the fabric).
**This must be added manually.**
- `node`: Represents an individual Node within the Matter network.
The only configuration parameter is `nodeId`.

View File

@ -8,10 +8,12 @@
"name": "code-gen",
"version": "0.1.0",
"dependencies": {
"@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/main": "v0.14.0",
"handlebars": "^4.7.8"
},
"devDependencies": {
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"ts-loader": "^9.4.4",
"ts-node": "^10.9.2",
"typescript": "^5.2.2"
@ -122,85 +124,85 @@
}
},
"node_modules/@matter/general": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-0rkdLhn/ETSW5w/MQqJQnYRPtOwFx2VKRHAiu5w1lHS7TIVtJ4emPLq2HMSiQ2HMAEDz9eYzfIO4OueEhCbW4A==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0.tgz",
"integrity": "sha512-lTaJgeWRlwr+4JZr0RcuwSMqbZMa/uPpDWG5P2RC7N/QvmL3fPoRjfdWYjCKXZkGfeG2XXIezQGCkp0z2BWLLQ==",
"dependencies": {
"@noble/curves": "^1.9.1"
}
},
"node_modules/@matter/main": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-tu/XAD3wK0kzI9PfXHUK5T0z4Y2vNUMZhbxNe7kSB4kHHjWCHitv1NMG0/uEIWRkSrm4/e82WCdJUrWxLgtm0g==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0.tgz",
"integrity": "sha512-fBXQtBm5+ySWg7yA4FyiCl/olbirWeBSt8ExXCBoMX2blByiYgKdj7HiHWUK0ksgNGXwh8qX62jS3+VFEdWNBQ==",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/node": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
},
"optionalDependencies": {
"@matter/nodejs": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/nodejs": "0.14.0"
}
},
"node_modules/@matter/model": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-NvXbecY1WUjXVuXSzZX78uP50rixPNKM8mKP6JRzUpO8sKys1JwZIiJl3kGKPdTuvwot2hwpFJURtnReDam/MA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0.tgz",
"integrity": "sha512-m4j+AY4lJCi9VE5bikUMwRIVuNWfjFIjhjD6VonTuYWVvqMGttWRQ3jTZV9qszkgur9YvOW2REmCJKvzB/KPqw==",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0"
}
},
"node_modules/@matter/node": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-dJt1HoFeomJKfBwcEkK2iAETLJfUVebYzCZ9Ie2qJ37uNc7DfhRomy/Dvj+deHS2Icfalm/COrkMiJQf2G3SvA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0.tgz",
"integrity": "sha512-QlK8Dg4YRD3db+CNZGnp0KVC1PsIbBwMzIAGh4TiO4ln68ltjRiOjNwYxb62F9YTSQhTuEQNxxiSfhK2nkYe2A==",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
}
},
"node_modules/@matter/nodejs": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-TTI7HfubBUQEZjcQAmcikoEerTfEt5iCy7ctSwzvt+M0PMToLyj3UrOgDEezezYw+hPzTBmitLWyb/VBnSpk0g==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0.tgz",
"integrity": "sha512-/XkBV5yX8ZH2SOkR4DDLoBivSONpEKk88QNf7AcTCf2wDadvM+hETsQ0Kli6U8pInpKTG/vghec+0bQ50nvO/A==",
"optional": true,
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/node": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@matter/protocol": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-y3PNdjtuiA1mAHnfpA3U30y29zQsxW3BOB3anYmGnpKTLTQia6hpmtp3FGHMBMi8rMNW5+PgQT9Gx2f/G+WwFg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0.tgz",
"integrity": "sha512-lmnXEXiO9/dEKOL/0n4jTXnEhwDwZvkRIu9QU6miuETMjBoG0spUkV6+EPWbE1+j/vXr21m65NoIvUtDoS2SKw==",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/types": "0.14.0"
}
},
"node_modules/@matter/types": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-72AgxSd1PQ5jxILMqt3JJ0Eg+duBekM/Bbjy7RIsXAB6eKPwXvvGypC4jjHn/FrAFbS7s0UVIvezT+xvxQM1gg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0.tgz",
"integrity": "sha512-xOm/Mbs2Uqota4jJwAjl8kzsz8l1wIA216FslzSpS4TmzCskQNI8j7JIROlGkJbdMeZvBhFLirYnwilLYfQ6zw==",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0"
}
},
"node_modules/@noble/curves": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz",
"integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
"dependencies": {
"@noble/hashes": "1.8.0"
},
@ -1099,6 +1101,37 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-organize-imports": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz",
"integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==",
"dev": true,
"peerDependencies": {
"prettier": ">=2.0",
"typescript": ">=2.9",
"vue-tsc": "^2.1.0"
},
"peerDependenciesMeta": {
"vue-tsc": {
"optional": true
}
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",

View File

@ -18,7 +18,7 @@
"prettier-plugin-organize-imports": "^4.1.0"
},
"dependencies": {
"@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/main": "v0.14.0",
"handlebars": "^4.7.8"
},
"files": [

View File

@ -8,9 +8,9 @@
"name": "matter-server",
"version": "0.1.0",
"dependencies": {
"@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@project-chip/matter.js": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/main": "v0.14.0",
"@matter/node": "v0.14.0",
"@project-chip/matter.js": "v0.14.0",
"uuid": "^9.0.1",
"ws": "^8.18.0",
"yargs": "^17.7.2"
@ -424,85 +424,93 @@
}
},
"node_modules/@matter/general": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-0rkdLhn/ETSW5w/MQqJQnYRPtOwFx2VKRHAiu5w1lHS7TIVtJ4emPLq2HMSiQ2HMAEDz9eYzfIO4OueEhCbW4A==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0.tgz",
"integrity": "sha512-lTaJgeWRlwr+4JZr0RcuwSMqbZMa/uPpDWG5P2RC7N/QvmL3fPoRjfdWYjCKXZkGfeG2XXIezQGCkp0z2BWLLQ==",
"license": "Apache-2.0",
"dependencies": {
"@noble/curves": "^1.9.1"
}
},
"node_modules/@matter/main": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-tu/XAD3wK0kzI9PfXHUK5T0z4Y2vNUMZhbxNe7kSB4kHHjWCHitv1NMG0/uEIWRkSrm4/e82WCdJUrWxLgtm0g==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0.tgz",
"integrity": "sha512-fBXQtBm5+ySWg7yA4FyiCl/olbirWeBSt8ExXCBoMX2blByiYgKdj7HiHWUK0ksgNGXwh8qX62jS3+VFEdWNBQ==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/node": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
},
"optionalDependencies": {
"@matter/nodejs": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/nodejs": "0.14.0"
}
},
"node_modules/@matter/model": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-NvXbecY1WUjXVuXSzZX78uP50rixPNKM8mKP6JRzUpO8sKys1JwZIiJl3kGKPdTuvwot2hwpFJURtnReDam/MA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0.tgz",
"integrity": "sha512-m4j+AY4lJCi9VE5bikUMwRIVuNWfjFIjhjD6VonTuYWVvqMGttWRQ3jTZV9qszkgur9YvOW2REmCJKvzB/KPqw==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0"
}
},
"node_modules/@matter/node": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-dJt1HoFeomJKfBwcEkK2iAETLJfUVebYzCZ9Ie2qJ37uNc7DfhRomy/Dvj+deHS2Icfalm/COrkMiJQf2G3SvA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0.tgz",
"integrity": "sha512-QlK8Dg4YRD3db+CNZGnp0KVC1PsIbBwMzIAGh4TiO4ln68ltjRiOjNwYxb62F9YTSQhTuEQNxxiSfhK2nkYe2A==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
}
},
"node_modules/@matter/nodejs": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-TTI7HfubBUQEZjcQAmcikoEerTfEt5iCy7ctSwzvt+M0PMToLyj3UrOgDEezezYw+hPzTBmitLWyb/VBnSpk0g==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0.tgz",
"integrity": "sha512-/XkBV5yX8ZH2SOkR4DDLoBivSONpEKk88QNf7AcTCf2wDadvM+hETsQ0Kli6U8pInpKTG/vghec+0bQ50nvO/A==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/node": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@matter/protocol": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-y3PNdjtuiA1mAHnfpA3U30y29zQsxW3BOB3anYmGnpKTLTQia6hpmtp3FGHMBMi8rMNW5+PgQT9Gx2f/G+WwFg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0.tgz",
"integrity": "sha512-lmnXEXiO9/dEKOL/0n4jTXnEhwDwZvkRIu9QU6miuETMjBoG0spUkV6+EPWbE1+j/vXr21m65NoIvUtDoS2SKw==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/types": "0.14.0"
}
},
"node_modules/@matter/types": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-72AgxSd1PQ5jxILMqt3JJ0Eg+duBekM/Bbjy7RIsXAB6eKPwXvvGypC4jjHn/FrAFbS7s0UVIvezT+xvxQM1gg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0.tgz",
"integrity": "sha512-xOm/Mbs2Uqota4jJwAjl8kzsz8l1wIA216FslzSpS4TmzCskQNI8j7JIROlGkJbdMeZvBhFLirYnwilLYfQ6zw==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0"
}
},
"node_modules/@noble/curves": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz",
"integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.8.0"
},
@ -517,6 +525,7 @@
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
@ -563,15 +572,16 @@
}
},
"node_modules/@project-chip/matter.js": {
"version": "0.14.0-alpha.0-20250531-7ed2d6da8",
"resolved": "https://registry.npmjs.org/@project-chip/matter.js/-/matter.js-0.14.0-alpha.0-20250531-7ed2d6da8.tgz",
"integrity": "sha512-CewVH1Ug1eSZBetCICpDs0HJbhi8uAKLMgdiMmkFrf2FwcQb9whdCwQ1FmB0cHThZCNsHLicStqv1S5poS9QkQ==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@project-chip/matter.js/-/matter.js-0.14.0.tgz",
"integrity": "sha512-w2aTSC3YbCbASBv/5YJvo9wIevRcfuuSP9mQwPnJVMlNiK8ocReVhS0L2xLqIrZVbA3SILATRnD6g7skT/G5wg==",
"license": "Apache-2.0",
"dependencies": {
"@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8"
"@matter/general": "0.14.0",
"@matter/model": "0.14.0",
"@matter/node": "0.14.0",
"@matter/protocol": "0.14.0",
"@matter/types": "0.14.0"
}
},
"node_modules/@tsconfig/node10": {

View File

@ -38,9 +38,9 @@
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/node": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@project-chip/matter.js": "v0.14.0-alpha.0-20250531-7ed2d6da8",
"@matter/main": "v0.14.0",
"@matter/node": "v0.14.0",
"@project-chip/matter.js": "v0.14.0",
"uuid": "^9.0.1",
"ws": "^8.18.0",
"yargs": "^17.7.2"

View File

@ -44,21 +44,20 @@ export abstract class Controller {
try {
const result = this.executeCommand(namespace, functionName, args || []);
if (result instanceof Promise) {
result
.then(asyncResult => {
this.ws.sendResponse(MessageType.ResultSuccess, id, asyncResult);
})
.catch(error => {
printError(logger, error, functionName);
this.ws.sendResponse(MessageType.ResultError, id, undefined, error.message);
});
const asyncResult = await result;
this.ws.sendResponse(MessageType.ResultSuccess, id, asyncResult);
} else {
this.ws.sendResponse(MessageType.ResultSuccess, id, result);
}
} catch (error) {
if (error instanceof Error) {
printError(logger, error, functionName);
this.ws.sendResponse(MessageType.ResultError, id, undefined, error.message);
let errorId: string | undefined;
// instances of a MatterError has an id property
if ("id" in error) {
errorId = (error as any).id;
}
this.ws.sendResponse(MessageType.ResultError, id, undefined, error.message, errorId);
} else {
logger.error(`Unexpected error executing function ${functionName}: ${error}`);
this.ws.sendResponse(MessageType.ResultError, id, undefined, String(error));

View File

@ -31,7 +31,6 @@ export interface Message {
}
export enum MessageType {
Result = "result",
ResultError = "resultError",
ResultSuccess = "resultSuccess",
}

View File

@ -6,8 +6,10 @@ import { hideBin } from "yargs/helpers";
import { BridgeController } from "./bridge/BridgeController";
import { ClientController } from "./client/ClientController";
import { Controller } from "./Controller";
import { Message, MessageType, Request } from "./MessageTypes";
import { Message, Request } from "./MessageTypes";
import { printError } from "./util/error";
import { toJSON } from "./util/Json";
const argv: any = yargs(hideBin(process.argv)).argv;
const logger = Logger.get("matter");
@ -72,7 +74,7 @@ const shutdownHandler = async (signal: string) => {
export interface WebSocketSession extends WebSocket {
controller?: Controller;
sendResponse(type: string, id: string, result?: any, error?: string): void;
sendResponse(type: string, id: string, result?: any, error?: string, errorId?: string): void;
sendEvent(type: string, data?: any): void;
}
@ -80,7 +82,7 @@ const socketPort = argv.port ? parseInt(argv.port) : 8888;
const wss: Server = new WebSocket.Server({ port: socketPort, host: argv.host });
wss.on("connection", (ws: WebSocketSession, req: IncomingMessage) => {
ws.sendResponse = (type: string, id: string, result?: any, error?: string) => {
ws.sendResponse = (type: string, id: string, result?: any, error?: string, errorId?: string) => {
const message: Message = {
type: "response",
message: {
@ -88,10 +90,11 @@ wss.on("connection", (ws: WebSocketSession, req: IncomingMessage) => {
id,
result,
error,
errorId,
},
};
logger.debug(`Sending response: ${Logger.toJSON(message)}`);
ws.send(Logger.toJSON(message));
logger.debug(`Sending response: ${toJSON(message)}`);
ws.send(toJSON(message));
};
ws.sendEvent = (type: string, data?: any) => {
@ -102,8 +105,8 @@ wss.on("connection", (ws: WebSocketSession, req: IncomingMessage) => {
data,
},
};
logger.debug(`Sending event: ${Logger.toJSON(message)}`);
ws.send(Logger.toJSON(message));
logger.debug(`Sending event: ${toJSON(message)}`);
ws.send(toJSON(message));
};
ws.on("open", () => {
@ -111,17 +114,9 @@ wss.on("connection", (ws: WebSocketSession, req: IncomingMessage) => {
});
ws.on("message", (message: string) => {
try {
const request: Request = JSON.parse(message);
if (ws.controller) {
void ws.controller.handleRequest(request).catch((error: Error) => {
ws.sendResponse(MessageType.ResultError, "", undefined, error.message);
});
}
} catch (error) {
if (error instanceof Error) {
ws.sendResponse(MessageType.ResultError, "", undefined, error.message);
}
const request: Request = JSON.parse(message);
if (ws.controller) {
void ws.controller.handleRequest(request);
}
});

View File

@ -1,6 +1,7 @@
import { Logger } from "@matter/general";
import { WebSocketSession } from "../app";
import { Controller } from "../Controller";
import { toJSON } from "../util/Json";
import { ControllerNode } from "./ControllerNode";
import { Clusters } from "./namespaces/Clusters";
import { Nodes } from "./namespaces/Nodes";
@ -53,7 +54,7 @@ export class ClientController extends Controller {
}
executeCommand(namespace: string, functionName: string, args: any[]): any | Promise<any> {
logger.debug(`Executing function ${namespace}.${functionName}(${Logger.toJSON(args)})`);
logger.debug(`Executing function ${namespace}.${functionName}(${toJSON(args)})`);
const controllerAny: any = this;

View File

@ -2,7 +2,7 @@ import { Logger } from "@matter/general";
import { ClusterId, ValidationError } from "@matter/main/types";
import { ClusterModel, MatterModel } from "@matter/model";
import { SupportedAttributeClient } from "@matter/protocol";
import { convertJsonDataWithModel } from "../../util/Json";
import { convertJsonDataWithModel, toJSON } from "../../util/Json";
import { capitalize } from "../../util/String";
import { ControllerNode } from "../ControllerNode";
@ -28,7 +28,7 @@ export class Clusters {
* @throws Error if the cluster or command is not found on the device.
*/
async command(nodeId: number, endpointId: number, clusterName: string, commandName: string, args: any) {
logger.debug(`command ${nodeId} ${endpointId} ${clusterName} ${commandName} ${Logger.toJSON(args)}`);
logger.debug(`command ${nodeId} ${endpointId} ${clusterName} ${commandName} ${toJSON(args)}`);
const device = this.controllerNode.getNode(nodeId).getDeviceById(endpointId);
if (device == undefined) {
throw new Error(`Endpoint ${endpointId} not found`);
@ -114,15 +114,15 @@ export class Clusters {
parsedValue = convertJsonDataWithModel(attribute, parsedValue);
await attributeClient.set(parsedValue);
console.log(
`Attribute ${attributeName} ${nodeId}/${endpointId}/${clusterName}/${attributeName} set to ${Logger.toJSON(value)}`,
`Attribute ${attributeName} ${nodeId}/${endpointId}/${clusterName}/${attributeName} set to ${toJSON(value)}`,
);
} catch (error) {
if (error instanceof ValidationError) {
throw new Error(
`Could not validate data for attribute ${attributeName} to ${Logger.toJSON(parsedValue)}: ${error}${error.fieldName !== undefined ? ` in field ${error.fieldName}` : ""}`,
`Could not validate data for attribute ${attributeName} to ${toJSON(parsedValue)}: ${error}${error.fieldName !== undefined ? ` in field ${error.fieldName}` : ""}`,
);
} else {
throw new Error(`Could not set attribute ${attributeName} to ${Logger.toJSON(parsedValue)}: ${error}`);
throw new Error(`Could not set attribute ${attributeName} to ${toJSON(parsedValue)}: ${error}`);
}
}
}

View File

@ -11,6 +11,9 @@ export function printError(logger: Logger, error: Error, functionName: string) {
if ("name" in error) {
logger.error(`Error name: ${(error as any).name}`);
}
if ("id" in error) {
logger.error(`Error id: ${(error as any).id}`);
}
// Fallback: log the entire error object in case there are other useful details
logger.error(`Full error object: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`);

View File

@ -17,6 +17,8 @@ import java.util.concurrent.ExecutionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.matter.internal.MatterBindingConstants;
import org.openhab.binding.matter.internal.client.MatterErrorCode;
import org.openhab.binding.matter.internal.client.MatterRequestException;
import org.openhab.binding.matter.internal.handler.ControllerHandler;
import org.openhab.binding.matter.internal.util.TranslationService;
import org.openhab.core.automation.annotation.ActionInput;
@ -72,9 +74,21 @@ public class MatterControllerActions implements ThingActions {
try {
handler.startScan(code).get();
return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_DEVICE_ADDED);
} catch (InterruptedException | ExecutionException e) {
return handler.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_PAIRING_FAILED)
+ e.getLocalizedMessage();
} catch (InterruptedException e) {
return handler.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_PAIRING_FAILED,
e.getLocalizedMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof MatterRequestException matterRequestException) {
MatterErrorCode errorCode = matterRequestException.getErrorCode();
if (errorCode != null) {
return handler.getTranslation(errorCode.getTranslationKey());
} else {
return handler.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_PAIRING_FAILED,
matterRequestException.getErrorMessage());
}
}
return handler.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_PAIRING_FAILED,
e.getLocalizedMessage());
}
}
return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER);

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2010-2025 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.matter.internal.client;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Error codes and translation keys for Matter errors events.
*
* @author Dan Cunningham - Initial contribution
*/
@NonNullByDefault
public enum MatterErrorCode {
COMMISSIONING("commissioning", "matterjs.error.commissioning"),
MAXIMUM_COMMISSIONED_FABRICS_REACHED("maximum-commissioned-fabrics-reached",
"matterjs.error.maximum-commissioned-fabrics-reached"),
COMMISSIONING_TIMEOUT("commissioning-timeout", "matterjs.error.commissioning-timeout"),
DEVICE_ALREADY_COMMISSIONED_TO_THIS_FABRIC("device-already-commissioned-to-this-fabric",
"matterjs.error.device-already-commissioned-to-this-fabric"),
FABRIC_LABEL_CONFLICT("fabric-label-conflict", "matterjs.error.fabric-label-conflict"),
WIFI_OR_THREAD_NETWORK_CREDENTIALS_NOT_CONFIGURED("wifi-or-thread-network-credentials-not-configured",
"matterjs.error.wifi-or-thread-network-credentials-not-configured"),
WIFI_NETWORK_SETUP_FAILED("wifi-network-setup-failed", "matterjs.error.wifi-network-setup-failed"),
THREAD_NETWORK_SETUP_FAILED("thread-network-setup-failed", "matterjs.error.thread-network-setup-failed"),
NODE_ID_CONFLICT("node-id-conflict", "matterjs.error.node-id-conflict"),
COMMISSIONABLE_DEVICE_DISCOVERY_FAILED("commissionable-device-discovery-failed",
"matterjs.error.commissionable-device-discovery-failed"),
OPERATIVE_CONNECTION_FAILED("operative-connection-failed", "matterjs.error.operative-connection-failed");
private final String errorId;
private final String translationKey;
MatterErrorCode(String errorId, String translationKey) {
this.errorId = errorId;
this.translationKey = translationKey;
}
public String getErrorId() {
return errorId;
}
public String getTranslationKey() {
return translationKey;
}
public static @Nullable MatterErrorCode fromErrorId(String errorId) {
for (MatterErrorCode error : values()) {
if (error.errorId.equals(errorId)) {
return error;
}
}
return null;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2025 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.matter.internal.client;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Exception thrown when a request to the Matter server fails.
*
* @author Dan Cunningham - Initial contribution
*/
@NonNullByDefault
public class MatterRequestException extends Exception {
private static final long serialVersionUID = 1L;
private final String errorMessage;
private final @Nullable MatterErrorCode errorCode;
public MatterRequestException(String message, @Nullable MatterErrorCode errorCode) {
super(message);
this.errorMessage = message;
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public @Nullable MatterErrorCode getErrorCode() {
return errorCode;
}
}

View File

@ -58,6 +58,7 @@ import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage;
import org.openhab.binding.matter.internal.client.dto.ws.Path;
import org.openhab.binding.matter.internal.client.dto.ws.Request;
import org.openhab.binding.matter.internal.client.dto.ws.Response;
import org.openhab.binding.matter.internal.client.dto.ws.ResponseType;
import org.openhab.binding.matter.internal.client.dto.ws.TriggerEvent;
import org.openhab.core.common.ThreadPoolManager;
import org.slf4j.Logger;
@ -261,8 +262,9 @@ public class MatterWebsocketClient implements WebSocketListener, MatterWebsocket
return;
}
logger.debug("result type: {} ", response.type);
if (!"resultSuccess".equals(response.type)) {
future.completeExceptionally(new Exception(response.error));
if (response.type != ResponseType.RESULT_SUCCESS) {
future.completeExceptionally(
new MatterRequestException(response.error, MatterErrorCode.fromErrorId(response.errorId)));
} else {
future.complete(response.result);
}

View File

@ -20,8 +20,9 @@ import com.google.gson.JsonElement;
* @author Dan Cunningham - Initial contribution
*/
public class Response {
public String type;
public ResponseType type;
public String id;
public JsonElement result;
public String error;
public String errorId;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2025 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.matter.internal.client.dto.ws;
import com.google.gson.annotations.SerializedName;
/**
* Websocket message response types.
*
* @author Dan Cunningham - Initial contribution
*/
public enum ResponseType {
@SerializedName("resultError")
RESULT_ERROR("resultError"),
@SerializedName("resultSuccess")
RESULT_SUCCESS("resultSuccess");
private final String value;
ResponseType(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
public String getValue() {
return value;
}
}

View File

@ -53,6 +53,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* Get all nodes that are commissioned / paired to this controller
*
* @return a future that returns a list of node IDs
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<List<BigInteger>> getCommissionedNodeIds() {
CompletableFuture<JsonElement> future = sendMessage("nodes", "listNodes", new Object[0]);
@ -69,6 +70,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param nodeId the node ID to initialize
* @param connectionTimeoutMilliseconds the timeout in milliseconds to wait for the node to connect
* @return a future that completes when the node is initialized
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> initializeNode(BigInteger nodeId, Integer connectionTimeoutMilliseconds) {
// add 1 second delay to the message timeout to allow the function to complete
@ -85,6 +87,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @param nodeId the node ID to request data for
* @return a future that completes when the data is requested
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> requestAllNodeData(BigInteger nodeId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "requestAllData", new Object[] { nodeId });
@ -99,6 +102,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param nodeId the node ID to request data for
* @param endpointId the endpoint ID to request data for
* @return a future that completes when the data is requested
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> requestEndpointData(BigInteger nodeId, Integer endpointId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "requestEndpointData",
@ -113,6 +117,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @param code the pairing code to pair with
* @return a future that completes when the node is paired (or fails)
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<BigInteger> pairNode(String code) {
String[] parts = code.trim().split(" ");
@ -134,6 +139,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @param nodeId the node ID to remove
* @return a future that completes when the node is removed
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> removeNode(BigInteger nodeId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "removeNode", new Object[] { nodeId });
@ -147,6 +153,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @param nodeId the node ID to reconnect
* @return a future that completes when the node is reconnected
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> reconnectNode(BigInteger nodeId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "reconnectNode", new Object[] { nodeId });
@ -161,6 +168,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param nodeId the node ID to get the pairing codes for
* @return a future that completes when the pairing codes are retrieved
* @throws JsonParseException when completing the future if the pairing codes cannot be deserialized
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<PairingCodes> enhancedCommissioningWindow(BigInteger nodeId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "enhancedCommissioningWindow",
@ -179,6 +187,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @param nodeId the node ID to disconnect
* @return a future that completes when the node is disconnected
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> disconnectNode(BigInteger nodeId) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "disconnectNode", new Object[] { nodeId });
@ -193,6 +202,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param nodeId the node ID to get the fabrics for
* @return a future that completes when the fabrics are retrieved or an exception is thrown
* @throws JsonParseException when completing the future if the fabrics cannot be deserialized
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<List<OperationalCredentialsCluster.FabricDescriptorStruct>> getFabrics(BigInteger nodeId) {
Object[] clusterArgs = { String.valueOf(nodeId) };
@ -214,6 +224,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param nodeId the node ID to remove the fabric from
* @param index the index of the fabric to remove
* @return a future that completes when the fabric is removed
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> removeFabric(BigInteger nodeId, Integer index) {
CompletableFuture<JsonElement> future = sendMessage("nodes", "removeFabric", new Object[] { nodeId, index });
@ -230,6 +241,8 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param clusterName the cluster name to send the command to
* @param command the command to send
* @return a future that completes when the command is sent
* @throws MatterRequestException if the request fails
* @throws JsonParseException when completing the future if the command cannot be deserialized
*/
public CompletableFuture<JsonElement> clusterCommand(BigInteger nodeId, Integer endpointId, String clusterName,
ClusterCommand command) {
@ -247,6 +260,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param attributeName the attribute name to write
* @param value the value to write
* @return a future that completes when the attribute is written
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<Void> clusterWriteAttribute(BigInteger nodeId, Integer endpointId, String clusterName,
String attributeName, String value) {
@ -266,6 +280,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param clusterId the cluster ID to read the cluster from
* @return a future that completes when the cluster is read
* @throws JsonParseException when completing the future if the cluster cannot be deserialized
* @throws MatterRequestException if the request fails
*/
public <T extends BaseCluster> CompletableFuture<T> readCluster(Class<T> type, BigInteger nodeId,
Integer endpointId, Integer clusterId) {
@ -289,6 +304,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
* @param clusterName the cluster name to read the attribute from
* @param attributeName the attribute name to read
* @return a future that completes when the attribute is read
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<String> clusterReadAttribute(BigInteger nodeId, Integer endpointId, String clusterName,
String attributeName) {
@ -304,6 +320,7 @@ public class MatterControllerClient extends MatterWebsocketClient {
*
* @return a future that completes when the session information is retrieved
* @throws JsonParseException when completing the future if the session information cannot be deserialized
* @throws MatterRequestException if the request fails
*/
public CompletableFuture<ActiveSessionInformation[]> getSessionInformation() {
CompletableFuture<JsonElement> future = sendMessage("nodes", "sessionInformation", new Object[0]);

View File

@ -270,8 +270,8 @@ public class ControllerHandler extends BaseBridgeHandler implements MatterClient
linkedNodes.keySet().forEach(nodeId -> updateNode(nodeId));
}
public String getTranslation(String key) {
return translationService.getTranslation(key);
public String getTranslation(String key, Object... args) {
return translationService.getTranslation(key, args);
}
public MatterControllerClient getClient() {

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.matter.internal.util;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
@ -51,10 +53,10 @@ public class TranslationService {
* @param key the key to get the translation for (with or without the @text/ prefix)
* @return the translation
*/
public String getTranslation(String key) {
public String getTranslation(String key, Object... args) {
String lookupKey = key.replace("@text/", "");
String result = translationProvider.getText(bundle, lookupKey, lookupKey, localeProvider.getLocale());
return result == null ? lookupKey : result;
String result = translationProvider.getText(bundle, lookupKey, lookupKey, localeProvider.getLocale(), args);
return result == null ? lookupKey + " " + Arrays.toString(args) : result;
}
public LocaleProvider getLocaleProvider() {

View File

@ -324,3 +324,17 @@ thing-action.result.pairing-failed = Failed to pair device: {0}
thing-status.detail.controller.waitingForData = Waiting for data
thing-status.detail.endpoint.thingNotReachable = Bridge reports device as not reachable
# matterjs error messages
matterjs.error.commissioning = General commissioning error
matterjs.error.maximum-commissioned-fabrics-reached = Maximum number of commissioned fabrics reached on device, please remove a fabric from another controller or reset the device
matterjs.error.commissioning-timeout = The commissioning process could not be finished within the maximum allowed time frame
matterjs.error.device-already-commissioned-to-this-fabric = The device is already commissioned to this fabric, either remove this fabric using another controller or reset the device
matterjs.error.fabric-label-conflict = The device is already commissioned to another fabric with the same label, please remove the fabric using another controller or reset the device
matterjs.error.wifi-or-thread-network-credentials-not-configured = Wi-Fi or Thread network credentials not configured
matterjs.error.wifi-network-setup-failed = Wi-Fi network setup failed
matterjs.error.thread-network-setup-failed = Thread network setup failed
matterjs.error.node-id-conflict = Node ID conflict, reset the device and try again
matterjs.error.commissionable-device-discovery-failed = The device could not be discovered using the provided code
matterjs.error.operative-connection-failed = The reconnection process for the device failed, please reset the device and try again