matterbridge 2.2.4 → 2.2.5-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +70 -334
- package/dist/index.js +1 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +46 -743
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -690
- package/dist/matterbridgeEndpointHelpers.js +9 -118
- package/dist/matterbridgePlatform.js +13 -185
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +7 -230
- package/dist/shelly.js +6 -121
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -45
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -40
- package/dist/utils/deepEqual.js +1 -65
- package/dist/utils/export.js +0 -1
- package/dist/utils/isvalid.js +0 -86
- package/dist/utils/network.js +5 -77
- package/dist/utils/parameter.js +0 -41
- package/dist/utils/wait.js +5 -48
- package/frontend/build/asset-manifest.json +80 -64
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/main.ea7910e9.css +2 -0
- package/frontend/build/static/css/main.ea7910e9.css.map +1 -0
- package/frontend/build/static/js/453.d855a71b.chunk.js +2 -0
- package/frontend/build/static/js/{453.abd36b29.chunk.js.map → 453.d855a71b.chunk.js.map} +1 -1
- package/frontend/build/static/js/main.b42b0a2a.js +115 -0
- package/frontend/build/static/js/{main.4a12038d.js.LICENSE.txt → main.b42b0a2a.js.LICENSE.txt} +12 -27
- package/frontend/build/static/js/{main.4a12038d.js.map → main.b42b0a2a.js.map} +1 -1
- package/frontend/build/static/media/roboto-cyrillic-300-normal.44340549d94d10899346.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-300-normal.89d0351bce4bc857dba6.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.86d5c52f4588f9f221d7.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.d67ac585bb6a05dbf71c.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.1fb2c6d685bfb888cfa3.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.36f79cc7e73a69da4438.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.e00802373a2c2db6b30d.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.fd3dfdd6cb1a9175b63d.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.a80c0d0719b1acb8f731.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.b9d87b04a9119d8d2fdf.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.31476620b88eec076438.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5e3f232f89080810567d.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.634ee2238bf30f362d52.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.d6c661248da2fde17768.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.361cdfd3a3f9c4bb09ca.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.6b08bc756cd72f5af9e8.woff +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.8300b541aa89b8301a6f.woff +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.fdd1f928a606aa116a44.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.98a717d5a38e77c0f657.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.ecd8572d631f20ff5bd5.woff +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.4fe733bc436afc295c24.woff +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.5c8100481d4e784afbf2.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.d23e03cf87ba44e5af6f.woff +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.d7dfd0b02cd8311e2a97.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.a88b77bb10633a8045e3.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.2d5875b032a1cca91eb2.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.a0baf7d6726d8f751a27.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.1964239c2800b6bd7e39.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.bef9c15c7164d6435aad.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.1aff9f4cd71608489b9a.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.eb28a447335ba6d54fcb.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.cb14f8e80cc69ddbac34.woff +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.db56943a88e4852343ae.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.50a0a61e29c19a2f05cb.woff +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.df1be0be92f6f19b8115.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.c320def131b39bceabd8.woff +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.bcfbe8accc968a375a8e.woff +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.6ddd1cfdbc5e74bcdab8.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.948c05192b1e64d931b1.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.0f86a30ca7e981fcfc99.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.2bfbba2d51a85c8702dd.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.8f02573e78730021ef49.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.aecaab4c4da2bf91377a.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.2d3c3ba6fe2d9c1026a5.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.8e656eff240311c6050a.woff2 +0 -0
- package/frontend/build/static/media/roboto-math-300-normal.90364ecfad5101ceb1a0.woff +0 -0
- package/frontend/build/static/media/roboto-math-300-normal.acc9c7c1d1fe3a1c7d44.woff2 +0 -0
- package/frontend/build/static/media/roboto-math-400-normal.3d3a272e5233c5fb1969.woff +0 -0
- package/frontend/build/static/media/roboto-math-400-normal.b60d9fba1e21da7497e6.woff2 +0 -0
- package/frontend/build/static/media/roboto-math-500-normal.41db483cb764343fca71.woff2 +0 -0
- package/frontend/build/static/media/roboto-math-500-normal.c3014a611cd9d8fa6252.woff +0 -0
- package/frontend/build/static/media/roboto-math-700-normal.a6fde3ddcb1629fd58b7.woff +0 -0
- package/frontend/build/static/media/roboto-math-700-normal.f6f4b54add6ab9d60a0f.woff2 +0 -0
- package/frontend/build/static/media/roboto-symbols-300-normal.52cdf8344b378f0c4580.woff +0 -0
- package/frontend/build/static/media/roboto-symbols-300-normal.616638ec44336b3da884.woff2 +0 -0
- package/frontend/build/static/media/roboto-symbols-400-normal.bb5b5d1459beb07bd3d5.woff2 +0 -0
- package/frontend/build/static/media/roboto-symbols-400-normal.f4f7e3bd8264f1a640cb.woff +0 -0
- package/frontend/build/static/media/roboto-symbols-500-normal.09b674875029289fd9a7.woff +0 -0
- package/frontend/build/static/media/roboto-symbols-500-normal.a5457b0ec984fd4cc8da.woff2 +0 -0
- package/frontend/build/static/media/roboto-symbols-700-normal.017e476ef02f62144169.woff +0 -0
- package/frontend/build/static/media/roboto-symbols-700-normal.634070e045ac99822c21.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.53f399e4522b647bafa7.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.6f0bf63e956c09377ef8.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.1cffe58e71a9109191a2.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.b1b8baa94fbcaa57d098.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.148734d63bd96c6e964f.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.72dbf2a25dd55b80b137.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.44a103f706f3ffe6a041.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.fa58a041a3336692af1e.woff +0 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -221
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -422
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -835
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -251
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -178
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -77
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -32
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -10
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -87
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -70
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts +0 -44
- package/dist/utils/parameter.d.ts.map +0 -1
- package/dist/utils/parameter.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -43
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/frontend/build/static/css/main.e52977d6.css +0 -2
- package/frontend/build/static/css/main.e52977d6.css.map +0 -1
- package/frontend/build/static/js/453.abd36b29.chunk.js +0 -2
- package/frontend/build/static/js/main.4a12038d.js +0 -115
- package/frontend/build/static/media/roboto-cyrillic-300-normal.1b79538ccd585c259996.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-300-normal.5f077fd7b977d1715acf.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.5d2930082227d172f62c.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.a9e19870cf6c4b973427.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.0ae2428323939af5e1ad.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.dd7bc8a52c6c70c5a3f5.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.3f6e1548bd5175a8c342.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.4fdfc29a10e7d4b7c527.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.795dbc8140e3fef82983.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.80947a31d23c70204b47.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.135d076fa32aa0b4d105.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5cec61a21cc20180fbe1.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.6de16332fda843a3dc3d.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.c0a0638f90b31d6454ba.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.4750292c47fa2bc6ac1a.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.ca247189fc12d00de361.woff +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.285f3e6261d8eb20417d.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.889beddda1c9bd9f97df.woff +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.160a791a8e4f46bca3cc.woff +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.2c32b1315be61477013a.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.60810e07c7b0273013aa.woff +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.f95e757c5483310f9c11.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.77dd370f2001e184ba0d.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.df87b053fae3d7ad5f7a.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.b590dbe5c639944366d1.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.d6049cb54aa6fbe14c42.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.16eb83b4a3b1ea994243.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.1df4abad55796d11a0c8.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.4a96ba31abcce0f5d52b.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.fd28d9c008bf3af1bed7.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.2dd6febad11502dec6a6.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.4abdc9fff4507f17d726.woff +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.b850f1ff581ea232fac9.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.c4bc0593c9954d79cb3a.woff +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.047a7839f69b209db815.woff +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.297d48e1b5a10c0831a9.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.68d40d6d01c6f85d24ba.woff +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.7077203b1982951ecf76.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.4535474e1cf8598695ad.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.9f6a16a7770c87b2042b.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.14982a9e4857a93b6dce.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.97cbc447d4a8d41a9543.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.27da5b36b6d3a16f53f4.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.2eeae187764baf05867d.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.06c30711d588145a4541.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.9a18d7bb9ff7a6af7b32.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.18841836e391d39e83a8.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.3c5bcdd0e69c4c3ffafe.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.c96b16e5c05c7b7c3e89.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.f5e7cea32756dfe7af40.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.0dc97c66f9b542d6fa17.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.d3f8e26d6c27de8102b6.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.090fabef926bdc0e9b9f.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.23b7b8a2524d2d4b637b.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.0a79a9fabfc32e33f360.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.35ed0597568ff6f19c16.woff +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -1,35 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 1.5.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// Node.js modules
|
|
24
1
|
import os from 'node:os';
|
|
25
2
|
import path from 'node:path';
|
|
26
3
|
import { promises as fs } from 'node:fs';
|
|
27
4
|
import EventEmitter from 'node:events';
|
|
28
|
-
// AnsiLogger module
|
|
29
5
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
30
|
-
// NodeStorage module
|
|
31
6
|
import { NodeStorageManager } from './storage/export.js';
|
|
32
|
-
// Matterbridge
|
|
33
7
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
|
|
34
8
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
35
9
|
import { PluginManager } from './pluginManager.js';
|
|
@@ -37,19 +11,14 @@ import { DeviceManager } from './deviceManager.js';
|
|
|
37
11
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
38
12
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
39
13
|
import { Frontend } from './frontend.js';
|
|
40
|
-
// @matter
|
|
41
14
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
42
15
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
43
16
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
44
17
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
45
18
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
46
|
-
// Default colors
|
|
47
19
|
const plg = '\u001B[38;5;33m';
|
|
48
20
|
const dev = '\u001B[38;5;79m';
|
|
49
21
|
const typ = '\u001B[38;5;207m';
|
|
50
|
-
/**
|
|
51
|
-
* Represents the Matterbridge application.
|
|
52
|
-
*/
|
|
53
22
|
export class Matterbridge extends EventEmitter {
|
|
54
23
|
systemInformation = {
|
|
55
24
|
interfaceName: '',
|
|
@@ -93,7 +62,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
93
62
|
shellySysUpdate: false,
|
|
94
63
|
shellyMainUpdate: false,
|
|
95
64
|
profile: getParameter('profile'),
|
|
96
|
-
loggerLevel: "info"
|
|
65
|
+
loggerLevel: "info",
|
|
97
66
|
fileLogger: false,
|
|
98
67
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
99
68
|
matterFileLogger: false,
|
|
@@ -130,11 +99,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
130
99
|
plugins;
|
|
131
100
|
devices;
|
|
132
101
|
frontend = new Frontend(this);
|
|
133
|
-
// Matterbridge storage
|
|
134
102
|
nodeStorage;
|
|
135
103
|
nodeContext;
|
|
136
104
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
137
|
-
// Cleanup
|
|
138
105
|
hasCleanupStarted = false;
|
|
139
106
|
initialized = false;
|
|
140
107
|
execRunningCount = 0;
|
|
@@ -147,70 +114,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
147
114
|
sigtermHandler;
|
|
148
115
|
exceptionHandler;
|
|
149
116
|
rejectionHandler;
|
|
150
|
-
// Matter environment
|
|
151
117
|
environment = Environment.default;
|
|
152
|
-
// Matter storage
|
|
153
118
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
154
119
|
matterStorageService;
|
|
155
120
|
matterStorageManager;
|
|
156
121
|
matterbridgeContext;
|
|
157
122
|
mattercontrollerContext;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
discriminator; // first server node discriminator
|
|
123
|
+
mdnsInterface;
|
|
124
|
+
ipv4address;
|
|
125
|
+
ipv6address;
|
|
126
|
+
port;
|
|
127
|
+
passcode;
|
|
128
|
+
discriminator;
|
|
165
129
|
serverNode;
|
|
166
130
|
aggregatorNode;
|
|
167
131
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
168
132
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
169
133
|
static instance;
|
|
170
|
-
// We load asyncronously so is private
|
|
171
134
|
constructor() {
|
|
172
135
|
super();
|
|
173
136
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Emits an event of the specified type with the provided arguments.
|
|
176
|
-
*
|
|
177
|
-
* @template K - The type of the event.
|
|
178
|
-
* @param {K} eventName - The name of the event to emit.
|
|
179
|
-
* @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
|
|
180
|
-
* @returns {boolean} - Returns true if the event had listeners, false otherwise.
|
|
181
|
-
*/
|
|
182
137
|
emit(eventName, ...args) {
|
|
183
138
|
return super.emit(eventName, ...args);
|
|
184
139
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Registers an event listener for the specified event type.
|
|
187
|
-
*
|
|
188
|
-
* @template K - The type of the event.
|
|
189
|
-
* @param {K} eventName - The name of the event to listen for.
|
|
190
|
-
* @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
|
|
191
|
-
* @returns {this} - Returns the instance of the Matterbridge class.
|
|
192
|
-
*/
|
|
193
140
|
on(eventName, listener) {
|
|
194
141
|
return super.on(eventName, listener);
|
|
195
142
|
}
|
|
196
|
-
/**
|
|
197
|
-
* Retrieves the list of Matterbridge devices.
|
|
198
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
199
|
-
*/
|
|
200
143
|
getDevices() {
|
|
201
144
|
return this.devices.array();
|
|
202
145
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Retrieves the list of registered plugins.
|
|
205
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
206
|
-
*/
|
|
207
146
|
getPlugins() {
|
|
208
147
|
return this.plugins.array();
|
|
209
148
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
212
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
213
|
-
*/
|
|
214
149
|
async setLogLevel(logLevel) {
|
|
215
150
|
if (this.log)
|
|
216
151
|
this.log.logLevel = logLevel;
|
|
@@ -224,31 +159,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
224
159
|
for (const plugin of this.plugins) {
|
|
225
160
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
226
161
|
continue;
|
|
227
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
228
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
162
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
163
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
164
|
+
}
|
|
165
|
+
let callbackLogLevel = "notice";
|
|
166
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
167
|
+
callbackLogLevel = "info";
|
|
168
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
169
|
+
callbackLogLevel = "debug";
|
|
236
170
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
237
171
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
238
172
|
}
|
|
239
|
-
/** ***********************************************************************************************************************************/
|
|
240
|
-
/** loadInstance() and cleanup() methods */
|
|
241
|
-
/** ***********************************************************************************************************************************/
|
|
242
|
-
/**
|
|
243
|
-
* Loads an instance of the Matterbridge class.
|
|
244
|
-
* If an instance already exists, return that instance.
|
|
245
|
-
*
|
|
246
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
247
|
-
* @returns The loaded Matterbridge instance.
|
|
248
|
-
*/
|
|
249
173
|
static async loadInstance(initialize = false) {
|
|
250
174
|
if (!Matterbridge.instance) {
|
|
251
|
-
// eslint-disable-next-line no-console
|
|
252
175
|
if (hasParameter('debug'))
|
|
253
176
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
254
177
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -257,14 +180,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
257
180
|
}
|
|
258
181
|
return Matterbridge.instance;
|
|
259
182
|
}
|
|
260
|
-
/**
|
|
261
|
-
* Call cleanup().
|
|
262
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
263
|
-
*
|
|
264
|
-
*/
|
|
265
183
|
async destroyInstance() {
|
|
266
184
|
this.log.info(`Destroy instance...`);
|
|
267
|
-
// Save server nodes to close
|
|
268
185
|
const servers = [];
|
|
269
186
|
if (this.bridgeMode === 'bridge') {
|
|
270
187
|
if (this.serverNode)
|
|
@@ -276,81 +193,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
276
193
|
servers.push(plugin.serverNode);
|
|
277
194
|
}
|
|
278
195
|
}
|
|
279
|
-
// Cleanup
|
|
280
196
|
await this.cleanup('destroying instance...', false);
|
|
281
|
-
// Close servers mdns service
|
|
282
197
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
283
198
|
for (const server of servers) {
|
|
284
199
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
285
200
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
286
201
|
}
|
|
287
|
-
// Wait for the cleanup to finish
|
|
288
202
|
await new Promise((resolve) => {
|
|
289
203
|
setTimeout(resolve, 1000);
|
|
290
204
|
});
|
|
291
205
|
}
|
|
292
|
-
/**
|
|
293
|
-
* Initializes the Matterbridge application.
|
|
294
|
-
*
|
|
295
|
-
* @remarks
|
|
296
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
297
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
298
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
299
|
-
*
|
|
300
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
301
|
-
*/
|
|
302
206
|
async initialize() {
|
|
303
|
-
// Set the restart mode
|
|
304
207
|
if (hasParameter('service'))
|
|
305
208
|
this.restartMode = 'service';
|
|
306
209
|
if (hasParameter('docker'))
|
|
307
210
|
this.restartMode = 'docker';
|
|
308
|
-
// Set the matterbridge directory
|
|
309
211
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
310
212
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
311
|
-
// Setup the matter environment
|
|
312
213
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
313
214
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
314
215
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
315
216
|
this.environment.vars.set('runtime.signals', false);
|
|
316
217
|
this.environment.vars.set('runtime.exitcode', false);
|
|
317
|
-
|
|
318
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
319
|
-
// Register process handlers
|
|
218
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
320
219
|
this.registerProcessHandlers();
|
|
321
|
-
// Initialize nodeStorage and nodeContext
|
|
322
220
|
try {
|
|
323
221
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
324
222
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
325
223
|
this.log.debug('Creating node storage context for matterbridge');
|
|
326
224
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
327
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
328
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
329
225
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
330
226
|
for (const key of keys) {
|
|
331
227
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
332
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
333
228
|
await this.nodeStorage?.storage.get(key);
|
|
334
229
|
}
|
|
335
230
|
const storages = await this.nodeStorage.getStorageNames();
|
|
336
231
|
for (const storage of storages) {
|
|
337
232
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
338
233
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
339
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
340
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
341
234
|
const keys = (await nodeContext?.storage.keys());
|
|
342
235
|
keys.forEach(async (key) => {
|
|
343
236
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
344
237
|
await nodeContext?.get(key);
|
|
345
238
|
});
|
|
346
239
|
}
|
|
347
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
348
240
|
this.log.debug('Creating node storage backup...');
|
|
349
241
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
350
242
|
this.log.debug('Created node storage backup');
|
|
351
243
|
}
|
|
352
244
|
catch (error) {
|
|
353
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
354
245
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
355
246
|
if (hasParameter('norestore')) {
|
|
356
247
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -365,46 +256,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
365
256
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
366
257
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
367
258
|
}
|
|
368
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
369
259
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
370
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
371
260
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
372
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
373
261
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
374
262
|
this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
375
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
376
263
|
if (hasParameter('logger')) {
|
|
377
264
|
const level = getParameter('logger');
|
|
378
265
|
if (level === 'debug') {
|
|
379
|
-
this.log.logLevel = "debug"
|
|
266
|
+
this.log.logLevel = "debug";
|
|
380
267
|
}
|
|
381
268
|
else if (level === 'info') {
|
|
382
|
-
this.log.logLevel = "info"
|
|
269
|
+
this.log.logLevel = "info";
|
|
383
270
|
}
|
|
384
271
|
else if (level === 'notice') {
|
|
385
|
-
this.log.logLevel = "notice"
|
|
272
|
+
this.log.logLevel = "notice";
|
|
386
273
|
}
|
|
387
274
|
else if (level === 'warn') {
|
|
388
|
-
this.log.logLevel = "warn"
|
|
275
|
+
this.log.logLevel = "warn";
|
|
389
276
|
}
|
|
390
277
|
else if (level === 'error') {
|
|
391
|
-
this.log.logLevel = "error"
|
|
278
|
+
this.log.logLevel = "error";
|
|
392
279
|
}
|
|
393
280
|
else if (level === 'fatal') {
|
|
394
|
-
this.log.logLevel = "fatal"
|
|
281
|
+
this.log.logLevel = "fatal";
|
|
395
282
|
}
|
|
396
283
|
else {
|
|
397
284
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
398
|
-
this.log.logLevel = "info"
|
|
285
|
+
this.log.logLevel = "info";
|
|
399
286
|
}
|
|
400
287
|
}
|
|
401
288
|
else {
|
|
402
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
289
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
403
290
|
}
|
|
404
291
|
this.frontend.logLevel = this.log.logLevel;
|
|
405
292
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
406
293
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
407
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
408
294
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
409
295
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
410
296
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -413,7 +299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
413
299
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
414
300
|
if (this.profile !== undefined)
|
|
415
301
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
416
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
417
302
|
if (hasParameter('matterlogger')) {
|
|
418
303
|
const level = getParameter('matterlogger');
|
|
419
304
|
if (level === 'debug') {
|
|
@@ -445,7 +330,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
445
330
|
Logger.format = MatterLogFormat.ANSI;
|
|
446
331
|
Logger.setLogger('default', this.createMatterLogger());
|
|
447
332
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
448
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
449
333
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
450
334
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
451
335
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -454,7 +338,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
454
338
|
});
|
|
455
339
|
}
|
|
456
340
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
457
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
458
341
|
if (hasParameter('mdnsinterface')) {
|
|
459
342
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
460
343
|
}
|
|
@@ -463,7 +346,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
463
346
|
if (this.mdnsInterface === '')
|
|
464
347
|
this.mdnsInterface = undefined;
|
|
465
348
|
}
|
|
466
|
-
// Validate mdnsInterface
|
|
467
349
|
if (this.mdnsInterface) {
|
|
468
350
|
const networkInterfaces = os.networkInterfaces();
|
|
469
351
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -477,7 +359,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
477
359
|
}
|
|
478
360
|
if (this.mdnsInterface)
|
|
479
361
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
480
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
481
362
|
if (hasParameter('ipv4address')) {
|
|
482
363
|
this.ipv4address = getParameter('ipv4address');
|
|
483
364
|
}
|
|
@@ -486,7 +367,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
486
367
|
if (this.ipv4address === '')
|
|
487
368
|
this.ipv4address = undefined;
|
|
488
369
|
}
|
|
489
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
490
370
|
if (hasParameter('ipv6address')) {
|
|
491
371
|
this.ipv6address = getParameter('ipv6address');
|
|
492
372
|
}
|
|
@@ -495,19 +375,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
495
375
|
if (this.ipv6address === '')
|
|
496
376
|
this.ipv6address = undefined;
|
|
497
377
|
}
|
|
498
|
-
// Initialize PluginManager
|
|
499
378
|
this.plugins = new PluginManager(this);
|
|
500
379
|
await this.plugins.loadFromStorage();
|
|
501
380
|
this.plugins.logLevel = this.log.logLevel;
|
|
502
|
-
// Initialize DeviceManager
|
|
503
381
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
504
382
|
this.devices.logLevel = this.log.logLevel;
|
|
505
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
506
383
|
for (const plugin of this.plugins) {
|
|
507
384
|
const packageJson = await this.plugins.parse(plugin);
|
|
508
385
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
509
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
510
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
511
386
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
512
387
|
try {
|
|
513
388
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -529,7 +404,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
529
404
|
await plugin.nodeContext.set('description', plugin.description);
|
|
530
405
|
await plugin.nodeContext.set('author', plugin.author);
|
|
531
406
|
}
|
|
532
|
-
// Log system info and create .matterbridge directory
|
|
533
407
|
await this.logNodeAndSystemInfo();
|
|
534
408
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
535
409
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -537,7 +411,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
537
411
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
538
412
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
539
413
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
540
|
-
// Check node version and throw error
|
|
541
414
|
const minNodeVersion = 18;
|
|
542
415
|
const nodeVersion = process.versions.node;
|
|
543
416
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -545,15 +418,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
545
418
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
546
419
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
547
420
|
}
|
|
548
|
-
// Parse command line
|
|
549
421
|
await this.parseCommandLine();
|
|
550
422
|
this.initialized = true;
|
|
551
423
|
}
|
|
552
|
-
/**
|
|
553
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
554
|
-
* @private
|
|
555
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
556
|
-
*/
|
|
557
424
|
async parseCommandLine() {
|
|
558
425
|
if (hasParameter('help')) {
|
|
559
426
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -665,7 +532,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
665
532
|
this.shutdown = true;
|
|
666
533
|
return;
|
|
667
534
|
}
|
|
668
|
-
// Start the matter storage and create the matterbridge context
|
|
669
535
|
try {
|
|
670
536
|
await this.startMatterStorage();
|
|
671
537
|
}
|
|
@@ -673,14 +539,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
673
539
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
674
540
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
675
541
|
}
|
|
676
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
677
542
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
678
543
|
this.initialized = true;
|
|
679
544
|
await this.shutdownProcessAndReset();
|
|
680
545
|
this.shutdown = true;
|
|
681
546
|
return;
|
|
682
547
|
}
|
|
683
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
684
548
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
685
549
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
686
550
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -705,37 +569,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
705
569
|
this.shutdown = true;
|
|
706
570
|
return;
|
|
707
571
|
}
|
|
708
|
-
// Initialize frontend
|
|
709
572
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
710
573
|
await this.frontend.start(getIntParameter('frontend'));
|
|
711
|
-
// Check in 30 seconds the latest versions
|
|
712
574
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
713
575
|
const { checkUpdates } = await import('./update.js');
|
|
714
576
|
checkUpdates(this);
|
|
715
577
|
}, 30 * 1000).unref();
|
|
716
|
-
// Check each 24 hours the latest versions
|
|
717
578
|
this.checkUpdateInterval = setInterval(async () => {
|
|
718
579
|
const { checkUpdates } = await import('./update.js');
|
|
719
580
|
checkUpdates(this);
|
|
720
581
|
}, 24 * 60 * 60 * 1000).unref();
|
|
721
|
-
// Start the matterbridge in mode test
|
|
722
582
|
if (hasParameter('test')) {
|
|
723
583
|
this.bridgeMode = 'bridge';
|
|
724
584
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
725
585
|
return;
|
|
726
586
|
}
|
|
727
|
-
// Start the matterbridge in mode controller
|
|
728
587
|
if (hasParameter('controller')) {
|
|
729
588
|
this.bridgeMode = 'controller';
|
|
730
589
|
await this.startController();
|
|
731
590
|
return;
|
|
732
591
|
}
|
|
733
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
734
592
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
735
593
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
736
594
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
737
595
|
}
|
|
738
|
-
// Start matterbridge in bridge mode
|
|
739
596
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
740
597
|
this.bridgeMode = 'bridge';
|
|
741
598
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -743,7 +600,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
743
600
|
await this.startBridge();
|
|
744
601
|
return;
|
|
745
602
|
}
|
|
746
|
-
// Start matterbridge in childbridge mode
|
|
747
603
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
748
604
|
this.bridgeMode = 'childbridge';
|
|
749
605
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -752,20 +608,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
752
608
|
return;
|
|
753
609
|
}
|
|
754
610
|
}
|
|
755
|
-
/**
|
|
756
|
-
* Asynchronously loads and starts the registered plugins.
|
|
757
|
-
*
|
|
758
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
759
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
760
|
-
*
|
|
761
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
762
|
-
*/
|
|
763
611
|
async startPlugins() {
|
|
764
|
-
// Check, load and start the plugins
|
|
765
612
|
for (const plugin of this.plugins) {
|
|
766
613
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
767
614
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
768
|
-
// Check if the plugin is available
|
|
769
615
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
770
616
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
771
617
|
plugin.enabled = false;
|
|
@@ -785,26 +631,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
785
631
|
plugin.addedDevices = undefined;
|
|
786
632
|
plugin.qrPairingCode = undefined;
|
|
787
633
|
plugin.manualPairingCode = undefined;
|
|
788
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
634
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
789
635
|
}
|
|
790
636
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
791
637
|
}
|
|
792
|
-
/**
|
|
793
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
794
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
795
|
-
*/
|
|
796
638
|
registerProcessHandlers() {
|
|
797
639
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
798
640
|
process.removeAllListeners('uncaughtException');
|
|
799
641
|
process.removeAllListeners('unhandledRejection');
|
|
800
642
|
this.exceptionHandler = async (error) => {
|
|
801
643
|
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
802
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
803
644
|
};
|
|
804
645
|
process.on('uncaughtException', this.exceptionHandler);
|
|
805
646
|
this.rejectionHandler = async (reason, promise) => {
|
|
806
647
|
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
807
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
808
648
|
};
|
|
809
649
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
810
650
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -817,9 +657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
817
657
|
};
|
|
818
658
|
process.on('SIGTERM', this.sigtermHandler);
|
|
819
659
|
}
|
|
820
|
-
/**
|
|
821
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
822
|
-
*/
|
|
823
660
|
deregisterProcesslHandlers() {
|
|
824
661
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
825
662
|
if (this.exceptionHandler)
|
|
@@ -836,17 +673,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
836
673
|
process.off('SIGTERM', this.sigtermHandler);
|
|
837
674
|
this.sigtermHandler = undefined;
|
|
838
675
|
}
|
|
839
|
-
/**
|
|
840
|
-
* Logs the node and system information.
|
|
841
|
-
*/
|
|
842
676
|
async logNodeAndSystemInfo() {
|
|
843
|
-
// IP address information
|
|
844
677
|
const networkInterfaces = os.networkInterfaces();
|
|
845
678
|
this.systemInformation.interfaceName = '';
|
|
846
679
|
this.systemInformation.ipv4Address = '';
|
|
847
680
|
this.systemInformation.ipv6Address = '';
|
|
848
681
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
849
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
850
682
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
851
683
|
continue;
|
|
852
684
|
if (!interfaceDetails) {
|
|
@@ -872,22 +704,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
872
704
|
break;
|
|
873
705
|
}
|
|
874
706
|
}
|
|
875
|
-
// Node information
|
|
876
707
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
877
708
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
878
709
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
879
710
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
880
|
-
// Host system information
|
|
881
711
|
this.systemInformation.hostname = os.hostname();
|
|
882
712
|
this.systemInformation.user = os.userInfo().username;
|
|
883
|
-
this.systemInformation.osType = os.type();
|
|
884
|
-
this.systemInformation.osRelease = os.release();
|
|
885
|
-
this.systemInformation.osPlatform = os.platform();
|
|
886
|
-
this.systemInformation.osArch = os.arch();
|
|
887
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
888
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
889
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
890
|
-
// Log the system information
|
|
713
|
+
this.systemInformation.osType = os.type();
|
|
714
|
+
this.systemInformation.osRelease = os.release();
|
|
715
|
+
this.systemInformation.osPlatform = os.platform();
|
|
716
|
+
this.systemInformation.osArch = os.arch();
|
|
717
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
718
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
719
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
891
720
|
this.log.debug('Host System Information:');
|
|
892
721
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
893
722
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -903,20 +732,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
903
732
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
904
733
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
905
734
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
906
|
-
// Home directory
|
|
907
735
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
908
736
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
909
737
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
910
|
-
// Package root directory
|
|
911
738
|
const { fileURLToPath } = await import('node:url');
|
|
912
739
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
913
740
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
914
741
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
915
742
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
916
|
-
// Global node_modules directory
|
|
917
743
|
if (this.nodeContext)
|
|
918
744
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
919
|
-
// First run of Matterbridge so the node storage is empty
|
|
920
745
|
if (this.globalModulesDirectory === '') {
|
|
921
746
|
try {
|
|
922
747
|
this.execRunningCount++;
|
|
@@ -932,20 +757,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
932
757
|
}
|
|
933
758
|
else
|
|
934
759
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
935
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
936
|
-
else {
|
|
937
|
-
this.getGlobalNodeModules()
|
|
938
|
-
.then(async (globalModulesDirectory) => {
|
|
939
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
940
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
941
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
942
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
943
|
-
})
|
|
944
|
-
.catch((error) => {
|
|
945
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
946
|
-
});
|
|
947
|
-
}*/
|
|
948
|
-
// Create the data directory .matterbridge in the home directory
|
|
949
760
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
950
761
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
951
762
|
try {
|
|
@@ -969,7 +780,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
969
780
|
}
|
|
970
781
|
}
|
|
971
782
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
972
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
973
783
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
974
784
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
975
785
|
try {
|
|
@@ -993,68 +803,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
993
803
|
}
|
|
994
804
|
}
|
|
995
805
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
996
|
-
// Matterbridge version
|
|
997
806
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
998
807
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
999
808
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
1000
809
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1001
|
-
// Matterbridge latest version
|
|
1002
810
|
if (this.nodeContext)
|
|
1003
811
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1004
812
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1005
|
-
// this.getMatterbridgeLatestVersion();
|
|
1006
|
-
// Current working directory
|
|
1007
813
|
const currentDir = process.cwd();
|
|
1008
814
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1009
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1010
815
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1011
816
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1012
817
|
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1015
|
-
*
|
|
1016
|
-
* @returns {Function} The MatterLogger function.
|
|
1017
|
-
*/
|
|
1018
818
|
createMatterLogger() {
|
|
1019
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
819
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1020
820
|
return (_level, formattedLog) => {
|
|
1021
821
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1022
822
|
const message = formattedLog.slice(65);
|
|
1023
823
|
matterLogger.logName = logger;
|
|
1024
824
|
switch (_level) {
|
|
1025
825
|
case MatterLogLevel.DEBUG:
|
|
1026
|
-
matterLogger.log("debug"
|
|
826
|
+
matterLogger.log("debug", message);
|
|
1027
827
|
break;
|
|
1028
828
|
case MatterLogLevel.INFO:
|
|
1029
|
-
matterLogger.log("info"
|
|
829
|
+
matterLogger.log("info", message);
|
|
1030
830
|
break;
|
|
1031
831
|
case MatterLogLevel.NOTICE:
|
|
1032
|
-
matterLogger.log("notice"
|
|
832
|
+
matterLogger.log("notice", message);
|
|
1033
833
|
break;
|
|
1034
834
|
case MatterLogLevel.WARN:
|
|
1035
|
-
matterLogger.log("warn"
|
|
835
|
+
matterLogger.log("warn", message);
|
|
1036
836
|
break;
|
|
1037
837
|
case MatterLogLevel.ERROR:
|
|
1038
|
-
matterLogger.log("error"
|
|
838
|
+
matterLogger.log("error", message);
|
|
1039
839
|
break;
|
|
1040
840
|
case MatterLogLevel.FATAL:
|
|
1041
|
-
matterLogger.log("fatal"
|
|
841
|
+
matterLogger.log("fatal", message);
|
|
1042
842
|
break;
|
|
1043
843
|
default:
|
|
1044
|
-
matterLogger.log("debug"
|
|
844
|
+
matterLogger.log("debug", message);
|
|
1045
845
|
break;
|
|
1046
846
|
}
|
|
1047
847
|
};
|
|
1048
848
|
}
|
|
1049
|
-
/**
|
|
1050
|
-
* Creates a Matter File Logger.
|
|
1051
|
-
*
|
|
1052
|
-
* @param {string} filePath - The path to the log file.
|
|
1053
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1054
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1055
|
-
*/
|
|
1056
849
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1057
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1058
850
|
let fileSize = 0;
|
|
1059
851
|
if (unlink) {
|
|
1060
852
|
try {
|
|
@@ -1103,21 +895,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1103
895
|
}
|
|
1104
896
|
};
|
|
1105
897
|
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1108
|
-
*/
|
|
1109
898
|
async restartProcess() {
|
|
1110
899
|
await this.cleanup('restarting...', true);
|
|
1111
900
|
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Shut down the process by exiting the current process.
|
|
1114
|
-
*/
|
|
1115
901
|
async shutdownProcess() {
|
|
1116
902
|
await this.cleanup('shutting down...', false);
|
|
1117
903
|
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Update matterbridge and and shut down the process.
|
|
1120
|
-
*/
|
|
1121
904
|
async updateProcess() {
|
|
1122
905
|
this.log.info('Updating matterbridge...');
|
|
1123
906
|
try {
|
|
@@ -1130,72 +913,51 @@ export class Matterbridge extends EventEmitter {
|
|
|
1130
913
|
this.frontend.wssSendRestartRequired();
|
|
1131
914
|
await this.cleanup('updating...', false);
|
|
1132
915
|
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Unregister all devices and shut down the process.
|
|
1135
|
-
*/
|
|
1136
916
|
async unregisterAndShutdownProcess() {
|
|
1137
917
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1138
918
|
for (const plugin of this.plugins) {
|
|
1139
919
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1140
920
|
}
|
|
1141
921
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1142
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
922
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1143
923
|
this.log.debug('Cleaning up and shutting down...');
|
|
1144
924
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1145
925
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Reset commissioning and shut down the process.
|
|
1148
|
-
*/
|
|
1149
926
|
async shutdownProcessAndReset() {
|
|
1150
927
|
await this.cleanup('shutting down with reset...', false);
|
|
1151
928
|
}
|
|
1152
|
-
/**
|
|
1153
|
-
* Factory reset and shut down the process.
|
|
1154
|
-
*/
|
|
1155
929
|
async shutdownProcessAndFactoryReset() {
|
|
1156
930
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1157
931
|
}
|
|
1158
|
-
/**
|
|
1159
|
-
* Cleans up the Matterbridge instance.
|
|
1160
|
-
* @param message - The cleanup message.
|
|
1161
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1162
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1163
|
-
*/
|
|
1164
932
|
async cleanup(message, restart = false) {
|
|
1165
933
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1166
934
|
this.hasCleanupStarted = true;
|
|
1167
935
|
this.log.info(message);
|
|
1168
|
-
// Clear the start matter interval
|
|
1169
936
|
if (this.startMatterInterval) {
|
|
1170
937
|
clearInterval(this.startMatterInterval);
|
|
1171
938
|
this.startMatterInterval = undefined;
|
|
1172
939
|
this.log.debug('Start matter interval cleared');
|
|
1173
940
|
}
|
|
1174
|
-
// Clear the check update timeout
|
|
1175
941
|
if (this.checkUpdateTimeout) {
|
|
1176
942
|
clearInterval(this.checkUpdateTimeout);
|
|
1177
943
|
this.checkUpdateTimeout = undefined;
|
|
1178
944
|
this.log.debug('Check update timeout cleared');
|
|
1179
945
|
}
|
|
1180
|
-
// Clear the check update interval
|
|
1181
946
|
if (this.checkUpdateInterval) {
|
|
1182
947
|
clearInterval(this.checkUpdateInterval);
|
|
1183
948
|
this.checkUpdateInterval = undefined;
|
|
1184
949
|
this.log.debug('Check update interval cleared');
|
|
1185
950
|
}
|
|
1186
|
-
// Clear the configure timeout
|
|
1187
951
|
if (this.configureTimeout) {
|
|
1188
952
|
clearTimeout(this.configureTimeout);
|
|
1189
953
|
this.configureTimeout = undefined;
|
|
1190
954
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1191
955
|
}
|
|
1192
|
-
// Clear the reachability timeout
|
|
1193
956
|
if (this.reachabilityTimeout) {
|
|
1194
957
|
clearTimeout(this.reachabilityTimeout);
|
|
1195
958
|
this.reachabilityTimeout = undefined;
|
|
1196
959
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1197
960
|
}
|
|
1198
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1199
961
|
for (const plugin of this.plugins) {
|
|
1200
962
|
if (!plugin.enabled || plugin.error)
|
|
1201
963
|
continue;
|
|
@@ -1206,10 +968,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1206
968
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1207
969
|
}
|
|
1208
970
|
}
|
|
1209
|
-
// Stopping matter server nodes
|
|
1210
971
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1211
972
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1212
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
973
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1213
974
|
if (this.bridgeMode === 'bridge') {
|
|
1214
975
|
if (this.serverNode) {
|
|
1215
976
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1225,7 +986,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1225
986
|
}
|
|
1226
987
|
}
|
|
1227
988
|
this.log.notice('Stopped matter server nodes');
|
|
1228
|
-
// Matter commisioning reset
|
|
1229
989
|
if (message === 'shutting down with reset...') {
|
|
1230
990
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1231
991
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1235,37 +995,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1235
995
|
await this.matterbridgeContext?.clearAll();
|
|
1236
996
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1237
997
|
}
|
|
1238
|
-
// Stop matter storage
|
|
1239
998
|
await this.stopMatterStorage();
|
|
1240
|
-
// Stop the frontend
|
|
1241
999
|
await this.frontend.stop();
|
|
1242
|
-
// Remove the matterfilelogger
|
|
1243
1000
|
try {
|
|
1244
1001
|
Logger.removeLogger('matterfilelogger');
|
|
1245
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1246
1002
|
}
|
|
1247
1003
|
catch (error) {
|
|
1248
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1249
1004
|
}
|
|
1250
|
-
// Serialize registeredDevices
|
|
1251
1005
|
if (this.nodeStorage && this.nodeContext) {
|
|
1252
|
-
/*
|
|
1253
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1254
|
-
this.log.info('Saving registered devices...');
|
|
1255
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1256
|
-
this.devices.forEach(async (device) => {
|
|
1257
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1258
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1259
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1260
|
-
});
|
|
1261
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1262
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1263
|
-
*/
|
|
1264
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1265
1006
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1266
1007
|
await this.nodeContext.close();
|
|
1267
1008
|
this.nodeContext = undefined;
|
|
1268
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1269
1009
|
for (const plugin of this.plugins) {
|
|
1270
1010
|
if (plugin.nodeContext) {
|
|
1271
1011
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1282,10 +1022,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1282
1022
|
}
|
|
1283
1023
|
this.plugins.clear();
|
|
1284
1024
|
this.devices.clear();
|
|
1285
|
-
// Factory reset
|
|
1286
1025
|
if (message === 'shutting down with factory reset...') {
|
|
1287
1026
|
try {
|
|
1288
|
-
// Delete old matter storage file and backup
|
|
1289
1027
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1290
1028
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1291
1029
|
await fs.unlink(file);
|
|
@@ -1299,7 +1037,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1299
1037
|
}
|
|
1300
1038
|
}
|
|
1301
1039
|
try {
|
|
1302
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1303
1040
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1304
1041
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1305
1042
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1313,7 +1050,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1313
1050
|
}
|
|
1314
1051
|
}
|
|
1315
1052
|
try {
|
|
1316
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1317
1053
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1318
1054
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1319
1055
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1328,13 +1064,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1328
1064
|
}
|
|
1329
1065
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1330
1066
|
}
|
|
1331
|
-
// Deregisters the process handlers
|
|
1332
1067
|
this.deregisterProcesslHandlers();
|
|
1333
1068
|
if (restart) {
|
|
1334
1069
|
if (message === 'updating...') {
|
|
1335
1070
|
this.log.info('Cleanup completed. Updating...');
|
|
1336
1071
|
Matterbridge.instance = undefined;
|
|
1337
|
-
this.emit('update');
|
|
1072
|
+
this.emit('update');
|
|
1338
1073
|
}
|
|
1339
1074
|
else if (message === 'restarting...') {
|
|
1340
1075
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1354,14 +1089,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1354
1089
|
this.log.debug('Cleanup already started...');
|
|
1355
1090
|
}
|
|
1356
1091
|
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1359
|
-
*
|
|
1360
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1361
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1362
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1363
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1364
|
-
*/
|
|
1365
1092
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1366
1093
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1367
1094
|
plugin.locked = true;
|
|
@@ -1374,13 +1101,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1374
1101
|
await this.startServerNode(plugin.serverNode);
|
|
1375
1102
|
}
|
|
1376
1103
|
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1379
|
-
*
|
|
1380
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1381
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1382
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1383
|
-
*/
|
|
1384
1104
|
async createDynamicPlugin(plugin, start = false) {
|
|
1385
1105
|
if (!plugin.locked) {
|
|
1386
1106
|
plugin.locked = true;
|
|
@@ -1392,13 +1112,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1392
1112
|
await this.startServerNode(plugin.serverNode);
|
|
1393
1113
|
}
|
|
1394
1114
|
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Starts the Matterbridge in bridge mode.
|
|
1397
|
-
* @private
|
|
1398
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1399
|
-
*/
|
|
1400
1115
|
async startBridge() {
|
|
1401
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1402
1116
|
if (!this.matterStorageManager)
|
|
1403
1117
|
throw new Error('No storage manager initialized');
|
|
1404
1118
|
if (!this.matterbridgeContext)
|
|
@@ -1436,9 +1150,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1436
1150
|
clearInterval(this.startMatterInterval);
|
|
1437
1151
|
this.startMatterInterval = undefined;
|
|
1438
1152
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1439
|
-
// Start the Matter server node
|
|
1440
1153
|
this.startServerNode(this.serverNode);
|
|
1441
|
-
// Configure the plugins
|
|
1442
1154
|
this.configureTimeout = setTimeout(async () => {
|
|
1443
1155
|
for (const plugin of this.plugins) {
|
|
1444
1156
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1456,7 +1168,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1456
1168
|
}
|
|
1457
1169
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1458
1170
|
}, 30 * 1000);
|
|
1459
|
-
// Setting reachability to true
|
|
1460
1171
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1461
1172
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1462
1173
|
if (this.aggregatorNode)
|
|
@@ -1465,11 +1176,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1465
1176
|
}, 60 * 1000);
|
|
1466
1177
|
}, 1000);
|
|
1467
1178
|
}
|
|
1468
|
-
/**
|
|
1469
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1470
|
-
* @private
|
|
1471
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1472
|
-
*/
|
|
1473
1179
|
async startChildbridge() {
|
|
1474
1180
|
if (!this.matterStorageManager)
|
|
1475
1181
|
throw new Error('No storage manager initialized');
|
|
@@ -1514,7 +1220,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1514
1220
|
clearInterval(this.startMatterInterval);
|
|
1515
1221
|
this.startMatterInterval = undefined;
|
|
1516
1222
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1517
|
-
// Configure the plugins
|
|
1518
1223
|
this.configureTimeout = setTimeout(async () => {
|
|
1519
1224
|
for (const plugin of this.plugins) {
|
|
1520
1225
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1551,9 +1256,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1551
1256
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1552
1257
|
continue;
|
|
1553
1258
|
}
|
|
1554
|
-
// Start the Matter server node
|
|
1555
1259
|
this.startServerNode(plugin.serverNode);
|
|
1556
|
-
// Setting reachability to true
|
|
1557
1260
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1558
1261
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
|
|
1559
1262
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1563,219 +1266,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1563
1266
|
}
|
|
1564
1267
|
}, 1000);
|
|
1565
1268
|
}
|
|
1566
|
-
/**
|
|
1567
|
-
* Starts the Matterbridge controller.
|
|
1568
|
-
* @private
|
|
1569
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1570
|
-
*/
|
|
1571
1269
|
async startController() {
|
|
1572
|
-
/*
|
|
1573
|
-
if (!this.storageManager) {
|
|
1574
|
-
this.log.error('No storage manager initialized');
|
|
1575
|
-
await this.cleanup('No storage manager initialized');
|
|
1576
|
-
return;
|
|
1577
|
-
}
|
|
1578
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1579
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1580
|
-
if (!this.mattercontrollerContext) {
|
|
1581
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1582
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1583
|
-
return;
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1587
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1588
|
-
this.log.info('Creating matter commissioning controller');
|
|
1589
|
-
this.commissioningController = new CommissioningController({
|
|
1590
|
-
autoConnect: false,
|
|
1591
|
-
});
|
|
1592
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1593
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1594
|
-
|
|
1595
|
-
this.log.info('Starting matter server');
|
|
1596
|
-
await this.matterServer.start();
|
|
1597
|
-
this.log.info('Matter server started');
|
|
1598
|
-
|
|
1599
|
-
if (hasParameter('pairingcode')) {
|
|
1600
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1601
|
-
const pairingCode = getParameter('pairingcode');
|
|
1602
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1603
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1604
|
-
|
|
1605
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1606
|
-
if (pairingCode !== undefined) {
|
|
1607
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1608
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1609
|
-
longDiscriminator = undefined;
|
|
1610
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1611
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1612
|
-
} else {
|
|
1613
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1614
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1615
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1616
|
-
}
|
|
1617
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1618
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1622
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1623
|
-
regulatoryCountryCode: 'XX',
|
|
1624
|
-
};
|
|
1625
|
-
const options = {
|
|
1626
|
-
commissioning: commissioningOptions,
|
|
1627
|
-
discovery: {
|
|
1628
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1629
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1630
|
-
},
|
|
1631
|
-
passcode: setupPin,
|
|
1632
|
-
} as NodeCommissioningOptions;
|
|
1633
|
-
this.log.info('Commissioning with options:', options);
|
|
1634
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1635
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1636
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1637
|
-
} // (hasParameter('pairingcode'))
|
|
1638
|
-
|
|
1639
|
-
if (hasParameter('unpairall')) {
|
|
1640
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1641
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1642
|
-
for (const nodeId of nodeIds) {
|
|
1643
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1644
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1645
|
-
}
|
|
1646
|
-
return;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
if (hasParameter('discover')) {
|
|
1650
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1651
|
-
// console.log(discover);
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1655
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1660
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1661
|
-
for (const nodeId of nodeIds) {
|
|
1662
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1663
|
-
|
|
1664
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1665
|
-
autoSubscribe: false,
|
|
1666
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1667
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1668
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1669
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1670
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1671
|
-
switch (info) {
|
|
1672
|
-
case NodeStateInformation.Connected:
|
|
1673
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1674
|
-
break;
|
|
1675
|
-
case NodeStateInformation.Disconnected:
|
|
1676
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1677
|
-
break;
|
|
1678
|
-
case NodeStateInformation.Reconnecting:
|
|
1679
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1680
|
-
break;
|
|
1681
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1682
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1683
|
-
break;
|
|
1684
|
-
case NodeStateInformation.StructureChanged:
|
|
1685
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1686
|
-
break;
|
|
1687
|
-
case NodeStateInformation.Decommissioned:
|
|
1688
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1689
|
-
break;
|
|
1690
|
-
default:
|
|
1691
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1692
|
-
break;
|
|
1693
|
-
}
|
|
1694
|
-
},
|
|
1695
|
-
});
|
|
1696
|
-
|
|
1697
|
-
node.logStructure();
|
|
1698
|
-
|
|
1699
|
-
// Get the interaction client
|
|
1700
|
-
this.log.info('Getting the interaction client');
|
|
1701
|
-
const interactionClient = await node.getInteractionClient();
|
|
1702
|
-
let cluster;
|
|
1703
|
-
let attributes;
|
|
1704
|
-
|
|
1705
|
-
// Log BasicInformationCluster
|
|
1706
|
-
cluster = BasicInformationCluster;
|
|
1707
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1708
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1709
|
-
});
|
|
1710
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1711
|
-
attributes.forEach((attribute) => {
|
|
1712
|
-
this.log.info(
|
|
1713
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1714
|
-
);
|
|
1715
|
-
});
|
|
1716
|
-
|
|
1717
|
-
// Log PowerSourceCluster
|
|
1718
|
-
cluster = PowerSourceCluster;
|
|
1719
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1720
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1721
|
-
});
|
|
1722
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1723
|
-
attributes.forEach((attribute) => {
|
|
1724
|
-
this.log.info(
|
|
1725
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1726
|
-
);
|
|
1727
|
-
});
|
|
1728
|
-
|
|
1729
|
-
// Log ThreadNetworkDiagnostics
|
|
1730
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1731
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1732
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1733
|
-
});
|
|
1734
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1735
|
-
attributes.forEach((attribute) => {
|
|
1736
|
-
this.log.info(
|
|
1737
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1738
|
-
);
|
|
1739
|
-
});
|
|
1740
|
-
|
|
1741
|
-
// Log SwitchCluster
|
|
1742
|
-
cluster = SwitchCluster;
|
|
1743
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1744
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1745
|
-
});
|
|
1746
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1747
|
-
attributes.forEach((attribute) => {
|
|
1748
|
-
this.log.info(
|
|
1749
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1750
|
-
);
|
|
1751
|
-
});
|
|
1752
|
-
|
|
1753
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1754
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1755
|
-
ignoreInitialTriggers: false,
|
|
1756
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1757
|
-
this.log.info(
|
|
1758
|
-
`***${db}Commissioning controller attributeChangedCallback version ${version}: attribute ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${attributeName}${db} changed to ${typeof value === 'object' ? debugStringify(value ?? { none: true }) : value}`,
|
|
1759
|
-
),
|
|
1760
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1761
|
-
this.log.info(
|
|
1762
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1763
|
-
);
|
|
1764
|
-
},
|
|
1765
|
-
});
|
|
1766
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1767
|
-
}
|
|
1768
|
-
*/
|
|
1769
1270
|
}
|
|
1770
|
-
/** ***********************************************************************************************************************************/
|
|
1771
|
-
/** Matter.js methods */
|
|
1772
|
-
/** ***********************************************************************************************************************************/
|
|
1773
|
-
/**
|
|
1774
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1775
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1776
|
-
*/
|
|
1777
1271
|
async startMatterStorage() {
|
|
1778
|
-
// Setup Matter storage
|
|
1779
1272
|
this.log.info(`Starting matter node storage...`);
|
|
1780
1273
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1781
1274
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1783,25 +1276,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1783
1276
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1784
1277
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1785
1278
|
this.log.info('Matter node storage started');
|
|
1786
|
-
// Backup matter storage since it is created/opened correctly
|
|
1787
1279
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1788
1280
|
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1791
|
-
*
|
|
1792
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1793
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1794
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1795
|
-
*/
|
|
1796
1281
|
async backupMatterStorage(storageName, backupName) {
|
|
1797
1282
|
this.log.info('Creating matter node storage backup...');
|
|
1798
1283
|
await copyDirectory(storageName, backupName);
|
|
1799
1284
|
this.log.info('Created matter node storage backup');
|
|
1800
1285
|
}
|
|
1801
|
-
/**
|
|
1802
|
-
* Stops the matter storage.
|
|
1803
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1804
|
-
*/
|
|
1805
1286
|
async stopMatterStorage() {
|
|
1806
1287
|
this.log.info('Closing matter node storage...');
|
|
1807
1288
|
this.matterStorageManager?.close();
|
|
@@ -1810,19 +1291,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1810
1291
|
this.matterbridgeContext = undefined;
|
|
1811
1292
|
this.log.info('Matter node storage closed');
|
|
1812
1293
|
}
|
|
1813
|
-
/**
|
|
1814
|
-
* Creates a server node storage context.
|
|
1815
|
-
*
|
|
1816
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1817
|
-
* @param {string} deviceName - The name of the device.
|
|
1818
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1819
|
-
* @param {number} vendorId - The vendor ID.
|
|
1820
|
-
* @param {string} vendorName - The vendor name.
|
|
1821
|
-
* @param {number} productId - The product ID.
|
|
1822
|
-
* @param {string} productName - The product name.
|
|
1823
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1824
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1825
|
-
*/
|
|
1826
1294
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1827
1295
|
const { randomBytes } = await import('node:crypto');
|
|
1828
1296
|
if (!this.matterStorageService)
|
|
@@ -1856,15 +1324,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1856
1324
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1857
1325
|
return storageContext;
|
|
1858
1326
|
}
|
|
1859
|
-
/**
|
|
1860
|
-
* Creates a server node.
|
|
1861
|
-
*
|
|
1862
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1863
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1864
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1865
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1866
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1867
|
-
*/
|
|
1868
1327
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1869
1328
|
const storeId = await storageContext.get('storeId');
|
|
1870
1329
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1874,33 +1333,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1874
1333
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1875
1334
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1876
1335
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1877
|
-
/**
|
|
1878
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1879
|
-
*/
|
|
1880
1336
|
const serverNode = await ServerNode.create({
|
|
1881
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1882
1337
|
id: storeId,
|
|
1883
|
-
// Provide Network relevant configuration like the port
|
|
1884
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1885
1338
|
network: {
|
|
1886
1339
|
listeningAddressIpv4: this.ipv4address,
|
|
1887
1340
|
listeningAddressIpv6: this.ipv6address,
|
|
1888
1341
|
port,
|
|
1889
1342
|
},
|
|
1890
|
-
// Provide Commissioning relevant settings
|
|
1891
|
-
// Optional for development/testing purposes
|
|
1892
1343
|
commissioning: {
|
|
1893
1344
|
passcode,
|
|
1894
1345
|
discriminator,
|
|
1895
1346
|
},
|
|
1896
|
-
// Provide Node announcement settings
|
|
1897
|
-
// Optional: If Ommitted some development defaults are used
|
|
1898
1347
|
productDescription: {
|
|
1899
1348
|
name: await storageContext.get('deviceName'),
|
|
1900
1349
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1901
1350
|
},
|
|
1902
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1903
|
-
// Optional: If Omitted some development defaults are used
|
|
1904
1351
|
basicInformation: {
|
|
1905
1352
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1906
1353
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1917,13 +1364,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1917
1364
|
},
|
|
1918
1365
|
});
|
|
1919
1366
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1920
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1921
1367
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1922
1368
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1923
1369
|
if (this.bridgeMode === 'bridge') {
|
|
1924
1370
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1925
1371
|
if (resetSessions)
|
|
1926
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1372
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1927
1373
|
this.matterbridgePaired = true;
|
|
1928
1374
|
}
|
|
1929
1375
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1931,19 +1377,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1931
1377
|
if (plugin) {
|
|
1932
1378
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1933
1379
|
if (resetSessions)
|
|
1934
|
-
plugin.sessionInformations = undefined;
|
|
1380
|
+
plugin.sessionInformations = undefined;
|
|
1935
1381
|
plugin.paired = true;
|
|
1936
1382
|
}
|
|
1937
1383
|
}
|
|
1938
1384
|
};
|
|
1939
|
-
/**
|
|
1940
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1941
|
-
* This means: It is added to the first fabric.
|
|
1942
|
-
*/
|
|
1943
1385
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1944
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1945
1386
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1946
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1947
1387
|
serverNode.lifecycle.online.on(async () => {
|
|
1948
1388
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1949
1389
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1991,7 +1431,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1991
1431
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1992
1432
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
1993
1433
|
});
|
|
1994
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1995
1434
|
serverNode.lifecycle.offline.on(() => {
|
|
1996
1435
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1997
1436
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2015,10 +1454,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2015
1454
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2016
1455
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2017
1456
|
});
|
|
2018
|
-
/**
|
|
2019
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2020
|
-
* information is needed.
|
|
2021
|
-
*/
|
|
2022
1457
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2023
1458
|
let action = '';
|
|
2024
1459
|
switch (fabricAction) {
|
|
@@ -2052,24 +1487,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1487
|
}
|
|
2053
1488
|
}
|
|
2054
1489
|
};
|
|
2055
|
-
/**
|
|
2056
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2057
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2058
|
-
*/
|
|
2059
1490
|
serverNode.events.sessions.opened.on((session) => {
|
|
2060
1491
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2061
1492
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2062
1493
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2063
1494
|
});
|
|
2064
|
-
/**
|
|
2065
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2066
|
-
*/
|
|
2067
1495
|
serverNode.events.sessions.closed.on((session) => {
|
|
2068
1496
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2069
1497
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2070
1498
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2071
1499
|
});
|
|
2072
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2073
1500
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2074
1501
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2075
1502
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2078,42 +1505,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2078
1505
|
this.log.info(`Created server node for ${storeId}`);
|
|
2079
1506
|
return serverNode;
|
|
2080
1507
|
}
|
|
2081
|
-
/**
|
|
2082
|
-
* Starts the specified server node.
|
|
2083
|
-
*
|
|
2084
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2085
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2086
|
-
*/
|
|
2087
1508
|
async startServerNode(matterServerNode) {
|
|
2088
1509
|
if (!matterServerNode)
|
|
2089
1510
|
return;
|
|
2090
1511
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2091
1512
|
await matterServerNode.start();
|
|
2092
1513
|
}
|
|
2093
|
-
/**
|
|
2094
|
-
* Stops the specified server node.
|
|
2095
|
-
*
|
|
2096
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2097
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2098
|
-
*/
|
|
2099
1514
|
async stopServerNode(matterServerNode) {
|
|
2100
1515
|
if (!matterServerNode)
|
|
2101
1516
|
return;
|
|
2102
1517
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2103
1518
|
try {
|
|
2104
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1519
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2105
1520
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2106
1521
|
}
|
|
2107
1522
|
catch (error) {
|
|
2108
1523
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2109
1524
|
}
|
|
2110
1525
|
}
|
|
2111
|
-
/**
|
|
2112
|
-
* Advertises the specified server node.
|
|
2113
|
-
*
|
|
2114
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2115
|
-
* @returns {Promise<{ qrPairingCode: string, manualPairingCode: string } | undefined>} A promise that resolves to the pairing codes if the server node is advertised, or undefined if not.
|
|
2116
|
-
*/
|
|
2117
1526
|
async advertiseServerNode(matterServerNode) {
|
|
2118
1527
|
if (matterServerNode) {
|
|
2119
1528
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2122,44 +1531,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2122
1531
|
return { qrPairingCode, manualPairingCode };
|
|
2123
1532
|
}
|
|
2124
1533
|
}
|
|
2125
|
-
/**
|
|
2126
|
-
* Stop advertise the specified server node.
|
|
2127
|
-
*
|
|
2128
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2129
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2130
|
-
*/
|
|
2131
1534
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2132
1535
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2133
1536
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2134
1537
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2135
1538
|
}
|
|
2136
1539
|
}
|
|
2137
|
-
/**
|
|
2138
|
-
* Creates an aggregator node with the specified storage context.
|
|
2139
|
-
*
|
|
2140
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2141
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2142
|
-
*/
|
|
2143
1540
|
async createAggregatorNode(storageContext) {
|
|
2144
1541
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2145
1542
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2146
1543
|
return aggregatorNode;
|
|
2147
1544
|
}
|
|
2148
|
-
/**
|
|
2149
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2150
|
-
*
|
|
2151
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2152
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2153
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2154
|
-
*/
|
|
2155
1545
|
async addBridgedEndpoint(pluginName, device) {
|
|
2156
|
-
// Check if the plugin is registered
|
|
2157
1546
|
const plugin = this.plugins.get(pluginName);
|
|
2158
1547
|
if (!plugin) {
|
|
2159
1548
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2160
1549
|
return;
|
|
2161
1550
|
}
|
|
2162
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2163
1551
|
if (this.bridgeMode === 'bridge') {
|
|
2164
1552
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2165
1553
|
if (!this.aggregatorNode)
|
|
@@ -2182,28 +1570,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2182
1570
|
plugin.registeredDevices++;
|
|
2183
1571
|
if (plugin.addedDevices !== undefined)
|
|
2184
1572
|
plugin.addedDevices++;
|
|
2185
|
-
// Add the device to the DeviceManager
|
|
2186
1573
|
this.devices.set(device);
|
|
2187
|
-
// Subscribe to the reachable$Changed event
|
|
2188
1574
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2189
1575
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2190
1576
|
}
|
|
2191
|
-
/**
|
|
2192
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2193
|
-
*
|
|
2194
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2195
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2196
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2197
|
-
*/
|
|
2198
1577
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2199
1578
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2200
|
-
// Check if the plugin is registered
|
|
2201
1579
|
const plugin = this.plugins.get(pluginName);
|
|
2202
1580
|
if (!plugin) {
|
|
2203
1581
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2204
1582
|
return;
|
|
2205
1583
|
}
|
|
2206
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2207
1584
|
if (this.bridgeMode === 'bridge') {
|
|
2208
1585
|
if (!this.aggregatorNode) {
|
|
2209
1586
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2218,7 +1595,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2218
1595
|
}
|
|
2219
1596
|
else if (this.bridgeMode === 'childbridge') {
|
|
2220
1597
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2221
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2222
1598
|
}
|
|
2223
1599
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2224
1600
|
if (!plugin.aggregatorNode) {
|
|
@@ -2233,16 +1609,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2233
1609
|
if (plugin.addedDevices !== undefined)
|
|
2234
1610
|
plugin.addedDevices--;
|
|
2235
1611
|
}
|
|
2236
|
-
// Remove the device from the DeviceManager
|
|
2237
1612
|
this.devices.remove(device);
|
|
2238
1613
|
}
|
|
2239
|
-
/**
|
|
2240
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2241
|
-
*
|
|
2242
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2243
|
-
* @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2244
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2245
|
-
*/
|
|
2246
1614
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2247
1615
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2248
1616
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2251,25 +1619,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2251
1619
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2252
1620
|
}
|
|
2253
1621
|
}
|
|
2254
|
-
/**
|
|
2255
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2256
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2257
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2258
|
-
*
|
|
2259
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2260
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2261
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2262
|
-
*/
|
|
2263
1622
|
async subscribeAttributeChanged(plugin, device) {
|
|
2264
1623
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2265
1624
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2266
|
-
/*
|
|
2267
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
|
|
2268
|
-
setTimeout(async () => {
|
|
2269
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
|
|
2270
|
-
await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
|
|
2271
|
-
}, 60000).unref();
|
|
2272
|
-
*/
|
|
2273
1625
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2274
1626
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2275
1627
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
@@ -2282,12 +1634,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2282
1634
|
});
|
|
2283
1635
|
}
|
|
2284
1636
|
}
|
|
2285
|
-
/**
|
|
2286
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2287
|
-
*
|
|
2288
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2289
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2290
|
-
*/
|
|
2291
1637
|
sanitizeFabricInformations(fabricInfo) {
|
|
2292
1638
|
return fabricInfo.map((info) => {
|
|
2293
1639
|
return {
|
|
@@ -2301,12 +1647,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2301
1647
|
};
|
|
2302
1648
|
});
|
|
2303
1649
|
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2306
|
-
*
|
|
2307
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2308
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2309
|
-
*/
|
|
2310
1650
|
sanitizeSessionInformation(sessionInfo) {
|
|
2311
1651
|
return sessionInfo
|
|
2312
1652
|
.filter((session) => session.isPeerActive)
|
|
@@ -2334,20 +1674,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2334
1674
|
};
|
|
2335
1675
|
});
|
|
2336
1676
|
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2339
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2340
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2341
|
-
*/
|
|
2342
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2343
1677
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2344
|
-
/*
|
|
2345
|
-
for (const child of aggregatorNode.parts) {
|
|
2346
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2347
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2348
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2349
|
-
}
|
|
2350
|
-
*/
|
|
2351
1678
|
}
|
|
2352
1679
|
getVendorIdName = (vendorId) => {
|
|
2353
1680
|
if (!vendorId)
|
|
@@ -2387,37 +1714,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2387
1714
|
}
|
|
2388
1715
|
return vendorName;
|
|
2389
1716
|
};
|
|
2390
|
-
/**
|
|
2391
|
-
* Spawns a child process with the given command and arguments.
|
|
2392
|
-
* @param {string} command - The command to execute.
|
|
2393
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2394
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2395
|
-
*/
|
|
2396
1717
|
async spawnCommand(command, args = []) {
|
|
2397
1718
|
const { spawn } = await import('node:child_process');
|
|
2398
|
-
/*
|
|
2399
|
-
npm > npm.cmd on windows
|
|
2400
|
-
cmd.exe ['dir'] on windows
|
|
2401
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2402
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2403
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2404
|
-
});
|
|
2405
|
-
|
|
2406
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2407
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2408
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2409
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2410
|
-
*/
|
|
2411
1719
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2412
1720
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2413
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2414
1721
|
const argstring = 'npm ' + args.join(' ');
|
|
2415
1722
|
args.splice(0, args.length, '/c', argstring);
|
|
2416
1723
|
command = 'cmd.exe';
|
|
2417
1724
|
}
|
|
2418
|
-
// Decide when using sudo on linux
|
|
2419
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2420
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2421
1725
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2422
1726
|
args.unshift(command);
|
|
2423
1727
|
command = 'sudo';
|
|
@@ -2476,4 +1780,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2476
1780
|
});
|
|
2477
1781
|
}
|
|
2478
1782
|
}
|
|
2479
|
-
//# sourceMappingURL=matterbridge.js.map
|