matterbridge 3.2.7-dev-20250908-3bb699e → 3.2.7-dev-20250913-9d0d095
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 +8 -2
- package/dist/frontend.js +99 -14
- package/dist/matterbridge.js +44 -7
- package/dist/matterbridgeBehaviors.js +51 -0
- package/dist/matterbridgeEndpoint.js +26 -3
- package/dist/{jest-utils → utils}/jestHelpers.js +73 -15
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.e691e19f.js → main.ee68a4ae.js} +3 -3
- package/frontend/build/static/js/{main.e691e19f.js.map → main.ee68a4ae.js.map} +1 -1
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +23 -15
- package/package.json +1 -1
- /package/frontend/build/static/js/{main.e691e19f.js.LICENSE.txt → main.ee68a4ae.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,7 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
8
8
|
<img src="bmc-button.svg" alt="Buy me a coffee" width="120">
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
-
## [3.2.7] - 2025-09
|
|
11
|
+
## [3.2.7] - 2025-09-12
|
|
12
12
|
|
|
13
13
|
### Breaking Changes
|
|
14
14
|
|
|
@@ -17,10 +17,16 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
17
17
|
|
|
18
18
|
### Added
|
|
19
19
|
|
|
20
|
+
- [jest]: Added Jest helpers module.
|
|
21
|
+
- [colorControl]: Added createEnhancedColorControlClusterServer (provisional to run compatibility tests on all controllers).
|
|
22
|
+
- [frontend]: Bumped `frontend` version to 2.7.6.
|
|
23
|
+
- [frontend]: Added api/view-diagnostic.
|
|
24
|
+
- [frontend]: Refactored the QRCode component for device with mode='server' (e.g. the Rvcs): added turn on and off pairing mode, resend mDns advertise, remove single fabrics, formatted manual paring code, copy to clipboard the manual pairing code and is fully web socket based. The main QRCode panel will have the same features (bridge mode and childbridge mode) in the next release.
|
|
25
|
+
|
|
20
26
|
### Changed
|
|
21
27
|
|
|
22
28
|
- [package]: Updated dependencies.
|
|
23
|
-
- [matterbridge.io]: Updated site.
|
|
29
|
+
- [matterbridge.io]: Updated web site [matterbridge.io](matterbridge.io).
|
|
24
30
|
|
|
25
31
|
### Fixed
|
|
26
32
|
|
package/dist/frontend.js
CHANGED
|
@@ -2,15 +2,17 @@ import { createServer } from 'node:http';
|
|
|
2
2
|
import https from 'node:https';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { existsSync, promises as fs } from 'node:fs';
|
|
5
|
+
import { existsSync, promises as fs, unlinkSync } from 'node:fs';
|
|
6
6
|
import EventEmitter from 'node:events';
|
|
7
|
+
import { appendFile } from 'node:fs/promises';
|
|
7
8
|
import express from 'express';
|
|
8
9
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
9
10
|
import multer from 'multer';
|
|
10
11
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
11
|
-
import { Logger, LogLevel as MatterLogLevel, Lifecycle } from '@matter/main';
|
|
12
|
+
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
|
|
12
13
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
13
|
-
import {
|
|
14
|
+
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/main/protocol';
|
|
15
|
+
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
|
|
14
16
|
import { plg } from './matterbridgeTypes.js';
|
|
15
17
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
16
18
|
import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
|
|
@@ -354,6 +356,52 @@ export class Frontend extends EventEmitter {
|
|
|
354
356
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
355
357
|
}
|
|
356
358
|
});
|
|
359
|
+
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
360
|
+
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
361
|
+
const serverNodes = [];
|
|
362
|
+
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
363
|
+
if (this.matterbridge.serverNode)
|
|
364
|
+
serverNodes.push(this.matterbridge.serverNode);
|
|
365
|
+
}
|
|
366
|
+
else if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
367
|
+
for (const plugin of this.matterbridge.getPlugins()) {
|
|
368
|
+
if (plugin.serverNode)
|
|
369
|
+
serverNodes.push(plugin.serverNode);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
for (const device of this.matterbridge.getDevices()) {
|
|
373
|
+
if (device.serverNode)
|
|
374
|
+
serverNodes.push(device.serverNode);
|
|
375
|
+
}
|
|
376
|
+
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log')))
|
|
377
|
+
unlinkSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'));
|
|
378
|
+
const diagnosticDestination = LogDestination({ name: 'diagnostic', level: MatterLogLevel.INFO, format: MatterLogFormat.formats.plain });
|
|
379
|
+
diagnosticDestination.write = async (text, _message) => {
|
|
380
|
+
await appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), text + '\n', { encoding: 'utf8' });
|
|
381
|
+
};
|
|
382
|
+
Logger.destinations.diagnostic = diagnosticDestination;
|
|
383
|
+
if (!diagnosticDestination.context) {
|
|
384
|
+
diagnosticDestination.context = Diagnostic.Context();
|
|
385
|
+
}
|
|
386
|
+
diagnosticDestination.context.run(() => diagnosticDestination.add(Diagnostic.message({
|
|
387
|
+
now: Time.now(),
|
|
388
|
+
facility: 'Server nodes:',
|
|
389
|
+
level: MatterLogLevel.INFO,
|
|
390
|
+
prefix: Logger.nestingLevel ? '⎸'.padEnd(Logger.nestingLevel * 2) : '',
|
|
391
|
+
values: [...serverNodes],
|
|
392
|
+
})));
|
|
393
|
+
delete Logger.destinations.diagnostic;
|
|
394
|
+
await wait(500);
|
|
395
|
+
try {
|
|
396
|
+
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
|
|
397
|
+
res.type('text/plain');
|
|
398
|
+
res.send(data.slice(29));
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
402
|
+
res.status(500).send('Error reading diagnostic log file.');
|
|
403
|
+
}
|
|
404
|
+
});
|
|
357
405
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
358
406
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
359
407
|
try {
|
|
@@ -667,14 +715,7 @@ export class Frontend extends EventEmitter {
|
|
|
667
715
|
}
|
|
668
716
|
getMatterDataFromDevice(device) {
|
|
669
717
|
if (device.mode === 'server' && device.serverNode) {
|
|
670
|
-
return
|
|
671
|
-
commissioned: device.serverNode.state.commissioning.commissioned,
|
|
672
|
-
qrPairingCode: device.serverNode.state.commissioning.pairingCodes.qrPairingCode,
|
|
673
|
-
manualPairingCode: device.serverNode.state.commissioning.pairingCodes.manualPairingCode,
|
|
674
|
-
fabricInformations: this.matterbridge.sanitizeFabricInformations(Object.values(device.serverNode.state.commissioning.fabrics)),
|
|
675
|
-
sessionInformations: this.matterbridge.sanitizeSessionInformation(Object.values(device.serverNode.state.sessions.sessions)),
|
|
676
|
-
serialNumber: device.serverNode.state.basicInformation.serialNumber,
|
|
677
|
-
};
|
|
718
|
+
return this.matterbridge.getServerNodeData(device.serverNode);
|
|
678
719
|
}
|
|
679
720
|
}
|
|
680
721
|
getClusterTextFromDevice(device) {
|
|
@@ -1267,6 +1308,50 @@ export class Frontend extends EventEmitter {
|
|
|
1267
1308
|
this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
|
|
1268
1309
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1269
1310
|
}
|
|
1311
|
+
else if (data.method === '/api/matter') {
|
|
1312
|
+
if (!isValidString(data.params.id)) {
|
|
1313
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter id in /api/matter' }));
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
let serverNode;
|
|
1317
|
+
if (data.params.id === 'Matterbridge')
|
|
1318
|
+
serverNode = this.matterbridge.serverNode;
|
|
1319
|
+
else
|
|
1320
|
+
serverNode = this.matterbridge.getPlugins().find((p) => p.serverNode && p.serverNode.id === data.params.id)?.serverNode || this.matterbridge.getDevices().find((d) => d.serverNode && d.serverNode.id === data.params.id)?.serverNode;
|
|
1321
|
+
if (!serverNode) {
|
|
1322
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Unknown server node id in /api/matter' }));
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
this.log.debug(`*Server node ${serverNode.id}: commissioned ${serverNode.state.commissioning.commissioned} upTime ${serverNode.state.generalDiagnostics.upTime}.`);
|
|
1326
|
+
if (data.params.server) {
|
|
1327
|
+
this.log.debug(`*Sending data for node ${data.params.id}`);
|
|
1328
|
+
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
|
|
1329
|
+
}
|
|
1330
|
+
if (data.params.commission) {
|
|
1331
|
+
await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1332
|
+
this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
|
|
1333
|
+
this.log.debug(`*Commissioning has been sent for node ${data.params.id}`);
|
|
1334
|
+
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
|
|
1335
|
+
}
|
|
1336
|
+
if (data.params.stopCommission) {
|
|
1337
|
+
await serverNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
1338
|
+
this.matterbridge.advertisingNodes.delete(serverNode.id);
|
|
1339
|
+
this.log.debug(`*Stop commissioning has been sent for node ${data.params.id}`);
|
|
1340
|
+
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: false } });
|
|
1341
|
+
}
|
|
1342
|
+
if (data.params.advertise) {
|
|
1343
|
+
await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
|
|
1344
|
+
this.log.debug(`*Advertising has been sent for node ${data.params.id}`);
|
|
1345
|
+
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
|
|
1346
|
+
}
|
|
1347
|
+
if (data.params.removeFabric) {
|
|
1348
|
+
if (serverNode.env.get(FabricManager).has(FabricIndex(data.params.removeFabric)))
|
|
1349
|
+
await serverNode.env.get(FabricManager).removeFabric(FabricIndex(data.params.removeFabric));
|
|
1350
|
+
this.log.debug(`*Removed fabric index ${data.params.removeFabric} for node ${data.params.id}`);
|
|
1351
|
+
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
|
|
1352
|
+
}
|
|
1353
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1354
|
+
}
|
|
1270
1355
|
else if (data.method === '/api/settings') {
|
|
1271
1356
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
|
|
1272
1357
|
}
|
|
@@ -1651,7 +1736,7 @@ export class Frontend extends EventEmitter {
|
|
|
1651
1736
|
}
|
|
1652
1737
|
}
|
|
1653
1738
|
catch (error) {
|
|
1654
|
-
this.log
|
|
1739
|
+
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
1655
1740
|
}
|
|
1656
1741
|
}
|
|
1657
1742
|
wssSendMessage(level, time, name, message) {
|
|
@@ -1681,11 +1766,11 @@ export class Frontend extends EventEmitter {
|
|
|
1681
1766
|
}
|
|
1682
1767
|
});
|
|
1683
1768
|
}
|
|
1684
|
-
wssSendRefreshRequired(changed = null) {
|
|
1769
|
+
wssSendRefreshRequired(changed = null, params = {}) {
|
|
1685
1770
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1686
1771
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1687
1772
|
if (client.readyState === WebSocket.OPEN) {
|
|
1688
|
-
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1773
|
+
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed, ...params } }));
|
|
1689
1774
|
}
|
|
1690
1775
|
});
|
|
1691
1776
|
}
|
package/dist/matterbridge.js
CHANGED
|
@@ -140,6 +140,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
140
140
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
141
141
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
142
142
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
143
|
+
advertisingNodes = new Map();
|
|
143
144
|
static instance;
|
|
144
145
|
constructor() {
|
|
145
146
|
super();
|
|
@@ -1394,15 +1395,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1394
1395
|
this.matterbridgeContext = undefined;
|
|
1395
1396
|
this.log.info('Matter node storage closed');
|
|
1396
1397
|
}
|
|
1397
|
-
async createServerNodeContext(
|
|
1398
|
+
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
1398
1399
|
const { randomBytes } = await import('node:crypto');
|
|
1399
1400
|
if (!this.matterStorageService)
|
|
1400
1401
|
throw new Error('No storage service initialized');
|
|
1401
|
-
this.log.info(`Creating server node storage context "${
|
|
1402
|
-
const storageManager = await this.matterStorageService.open(
|
|
1402
|
+
this.log.info(`Creating server node storage context "${storeId}.persist" for ${storeId}...`);
|
|
1403
|
+
const storageManager = await this.matterStorageService.open(storeId);
|
|
1403
1404
|
const storageContext = storageManager.createContext('persist');
|
|
1404
1405
|
const random = randomBytes(8).toString('hex');
|
|
1405
|
-
await storageContext.set('storeId',
|
|
1406
|
+
await storageContext.set('storeId', storeId);
|
|
1406
1407
|
await storageContext.set('deviceName', deviceName);
|
|
1407
1408
|
await storageContext.set('deviceType', deviceType);
|
|
1408
1409
|
await storageContext.set('vendorId', vendorId);
|
|
@@ -1417,10 +1418,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1417
1418
|
await storageContext.set('softwareVersionString', isValidString(this.matterbridgeVersion, 5, 64) ? this.matterbridgeVersion : '1.0.0');
|
|
1418
1419
|
await storageContext.set('hardwareVersion', isValidNumber(parseVersionString(this.systemInformation.osRelease), 0, UINT16_MAX) ? parseVersionString(this.systemInformation.osRelease) : 1);
|
|
1419
1420
|
await storageContext.set('hardwareVersionString', isValidString(this.systemInformation.osRelease, 5, 64) ? this.systemInformation.osRelease : '1.0.0');
|
|
1420
|
-
this.log.debug(`Created server node storage context "${
|
|
1421
|
+
this.log.debug(`Created server node storage context "${storeId}.persist" for ${storeId}:`);
|
|
1421
1422
|
this.log.debug(`- storeId: ${await storageContext.get('storeId')}`);
|
|
1422
1423
|
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
|
|
1423
1424
|
this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1425
|
+
this.log.debug(`- vendorId: ${await storageContext.get('vendorId')}`);
|
|
1426
|
+
this.log.debug(`- vendorName: ${await storageContext.get('vendorName')}`);
|
|
1427
|
+
this.log.debug(`- productId: ${await storageContext.get('productId')}`);
|
|
1428
|
+
this.log.debug(`- productName: ${await storageContext.get('productName')}`);
|
|
1429
|
+
this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
|
|
1430
|
+
this.log.debug(`- productLabel: ${await storageContext.get('productLabel')}`);
|
|
1424
1431
|
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
|
|
1425
1432
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1426
1433
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
@@ -1486,12 +1493,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1486
1493
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1487
1494
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
1488
1495
|
this.startEndAdvertiseTimer(serverNode);
|
|
1496
|
+
this.advertisingNodes.set(storeId, Date.now());
|
|
1489
1497
|
}
|
|
1490
1498
|
else {
|
|
1491
1499
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
1500
|
+
this.advertisingNodes.delete(storeId);
|
|
1492
1501
|
}
|
|
1493
1502
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1494
1503
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1504
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1495
1505
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
1496
1506
|
this.emit('online', storeId);
|
|
1497
1507
|
});
|
|
@@ -1500,6 +1510,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1500
1510
|
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
1501
1511
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1502
1512
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1513
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1503
1514
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
1504
1515
|
this.emit('offline', storeId);
|
|
1505
1516
|
});
|
|
@@ -1507,6 +1518,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1507
1518
|
let action = '';
|
|
1508
1519
|
switch (fabricAction) {
|
|
1509
1520
|
case FabricAction.Added:
|
|
1521
|
+
this.advertisingNodes.delete(storeId);
|
|
1522
|
+
clearTimeout(this.endAdvertiseTimeout);
|
|
1523
|
+
this.endAdvertiseTimeout = undefined;
|
|
1524
|
+
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
1510
1525
|
action = 'added';
|
|
1511
1526
|
break;
|
|
1512
1527
|
case FabricAction.Removed:
|
|
@@ -1518,18 +1533,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1518
1533
|
}
|
|
1519
1534
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
1520
1535
|
this.frontend.wssSendRefreshRequired('fabrics');
|
|
1536
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1521
1537
|
});
|
|
1522
1538
|
serverNode.events.sessions.opened.on((session) => {
|
|
1523
1539
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
1524
1540
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
1541
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1525
1542
|
});
|
|
1526
1543
|
serverNode.events.sessions.closed.on((session) => {
|
|
1527
1544
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
1528
1545
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
1546
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1529
1547
|
});
|
|
1530
1548
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
1531
1549
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
1532
1550
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
1551
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1533
1552
|
});
|
|
1534
1553
|
this.log.info(`Created server node for ${storeId}`);
|
|
1535
1554
|
return serverNode;
|
|
@@ -1548,10 +1567,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
1548
1567
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1549
1568
|
this.frontend.wssSendRefreshRequired('fabrics');
|
|
1550
1569
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
1551
|
-
this.frontend.
|
|
1552
|
-
this.
|
|
1570
|
+
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(matterServerNode) } });
|
|
1571
|
+
this.frontend.wssSendSnackbarMessage(`Advertising stopped.`, 0);
|
|
1572
|
+
this.log.notice(`Advertising stopped.`);
|
|
1553
1573
|
}, 15 * 60 * 1000).unref();
|
|
1554
1574
|
}
|
|
1575
|
+
getServerNodeData(serverNode) {
|
|
1576
|
+
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
1577
|
+
return {
|
|
1578
|
+
id: serverNode.id,
|
|
1579
|
+
commissioned: serverNode.state.commissioning.commissioned,
|
|
1580
|
+
advertising: advertiseTime > Date.now() - 15 * 60 * 1000,
|
|
1581
|
+
advertiseTime,
|
|
1582
|
+
windowStatus: serverNode.state.administratorCommissioning.windowStatus,
|
|
1583
|
+
qrPairingCode: serverNode.state.commissioning.pairingCodes.qrPairingCode,
|
|
1584
|
+
manualPairingCode: serverNode.state.commissioning.pairingCodes.manualPairingCode,
|
|
1585
|
+
fabricInformations: this.sanitizeFabricInformations(Object.values(serverNode.state.commissioning.fabrics)),
|
|
1586
|
+
sessionInformations: this.sanitizeSessionInformation(Object.values(serverNode.state.sessions.sessions)),
|
|
1587
|
+
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1555
1590
|
async startServerNode(matterServerNode) {
|
|
1556
1591
|
if (!matterServerNode)
|
|
1557
1592
|
return;
|
|
@@ -1574,6 +1609,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1574
1609
|
if (matterServerNode) {
|
|
1575
1610
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1576
1611
|
const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
|
|
1612
|
+
this.advertisingNodes.set(matterServerNode.id, Date.now());
|
|
1577
1613
|
this.log.notice(`Started advertising for ${matterServerNode.id} with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
|
|
1578
1614
|
return { qrPairingCode, manualPairingCode };
|
|
1579
1615
|
}
|
|
@@ -1581,6 +1617,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1581
1617
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
1582
1618
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
1583
1619
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
1620
|
+
this.advertisingNodes.delete(matterServerNode.id);
|
|
1584
1621
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
1585
1622
|
}
|
|
1586
1623
|
}
|
|
@@ -135,6 +135,57 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
|
|
|
135
135
|
super.moveToColorTemperature(request);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
+
export class MatterbridgeEnhancedColorControlServer extends ColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.EnhancedHue, ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature) {
|
|
139
|
+
moveToHue(request) {
|
|
140
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
141
|
+
device.log.info(`Setting hue to ${request.hue} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
142
|
+
device.commandHandler.executeHandler('moveToHue', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
143
|
+
device.log.debug(`MatterbridgeColorControlServer: moveToHue called`);
|
|
144
|
+
super.moveToHue(request);
|
|
145
|
+
}
|
|
146
|
+
enhancedMoveToHue(request) {
|
|
147
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
148
|
+
device.log.info(`Setting enhanced hue to ${request.enhancedHue} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
149
|
+
device.commandHandler.executeHandler('enhancedMoveToHue', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
150
|
+
device.log.debug(`MatterbridgeColorControlServer: enhancedMoveToHue called`);
|
|
151
|
+
super.enhancedMoveToHue(request);
|
|
152
|
+
}
|
|
153
|
+
moveToSaturation(request) {
|
|
154
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
155
|
+
device.log.info(`Setting saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
156
|
+
device.commandHandler.executeHandler('moveToSaturation', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
157
|
+
device.log.debug(`MatterbridgeColorControlServer: moveToSaturation called`);
|
|
158
|
+
super.moveToSaturation(request);
|
|
159
|
+
}
|
|
160
|
+
moveToHueAndSaturation(request) {
|
|
161
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
162
|
+
device.log.info(`Setting hue to ${request.hue} and saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
163
|
+
device.commandHandler.executeHandler('moveToHueAndSaturation', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
164
|
+
device.log.debug(`MatterbridgeColorControlServer: moveToHueAndSaturation called`);
|
|
165
|
+
super.moveToHueAndSaturation(request);
|
|
166
|
+
}
|
|
167
|
+
enhancedMoveToHueAndSaturation(request) {
|
|
168
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
169
|
+
device.log.info(`Setting enhanced hue to ${request.enhancedHue} and saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
170
|
+
device.commandHandler.executeHandler('enhancedMoveToHueAndSaturation', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
171
|
+
device.log.debug(`MatterbridgeColorControlServer: enhancedMoveToHueAndSaturation called`);
|
|
172
|
+
super.enhancedMoveToHueAndSaturation(request);
|
|
173
|
+
}
|
|
174
|
+
moveToColor(request) {
|
|
175
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
176
|
+
device.log.info(`Setting color to ${request.colorX}, ${request.colorY} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
177
|
+
device.commandHandler.executeHandler('moveToColor', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
178
|
+
device.log.debug(`MatterbridgeColorControlServer: moveToColor called`);
|
|
179
|
+
super.moveToColor(request);
|
|
180
|
+
}
|
|
181
|
+
moveToColorTemperature(request) {
|
|
182
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
183
|
+
device.log.info(`Setting color temperature to ${request.colorTemperatureMireds} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
184
|
+
device.commandHandler.executeHandler('moveToColorTemperature', { request, cluster: ColorControlServer.id, attributes: this.state, endpoint: this.endpoint });
|
|
185
|
+
device.log.debug(`MatterbridgeColorControlServer: moveToColorTemperature called`);
|
|
186
|
+
super.moveToColorTemperature(request);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
138
189
|
export class MatterbridgeLiftWindowCoveringServer extends WindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift) {
|
|
139
190
|
upOrOpen() {
|
|
140
191
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
@@ -59,7 +59,7 @@ import { ResourceMonitoring } from '@matter/main/clusters/resource-monitoring';
|
|
|
59
59
|
import { ThermostatUserInterfaceConfigurationServer } from '@matter/main/behaviors/thermostat-user-interface-configuration';
|
|
60
60
|
import { AnsiLogger, CYAN, YELLOW, db, debugStringify, hk, or, zb } from './logger/export.js';
|
|
61
61
|
import { isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
62
|
-
import { MatterbridgeServer, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, MatterbridgeDeviceEnergyManagementServer, MatterbridgeActivatedCarbonFilterMonitoringServer, MatterbridgeHepaFilterMonitoringServer, } from './matterbridgeBehaviors.js';
|
|
62
|
+
import { MatterbridgeServer, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, MatterbridgeDeviceEnergyManagementServer, MatterbridgeActivatedCarbonFilterMonitoringServer, MatterbridgeHepaFilterMonitoringServer, MatterbridgeEnhancedColorControlServer, } from './matterbridgeBehaviors.js';
|
|
63
63
|
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, invokeBehaviorCommand, triggerEvent, featuresFor, } from './matterbridgeEndpointHelpers.js';
|
|
64
64
|
export class MatterbridgeEndpoint extends Endpoint {
|
|
65
65
|
static logLevel = "info";
|
|
@@ -586,6 +586,29 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
586
586
|
});
|
|
587
587
|
return this;
|
|
588
588
|
}
|
|
589
|
+
createEnhancedColorControlClusterServer(currentX = 0, currentY = 0, enhancedCurrentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
590
|
+
this.behaviors.require(MatterbridgeEnhancedColorControlServer.with(ColorControl.Feature.Xy, ColorControl.Feature.HueSaturation, ColorControl.Feature.EnhancedHue, ColorControl.Feature.ColorTemperature), {
|
|
591
|
+
colorMode: ColorControl.ColorMode.CurrentHueAndCurrentSaturation,
|
|
592
|
+
enhancedColorMode: ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation,
|
|
593
|
+
colorCapabilities: { xy: true, hueSaturation: true, colorLoop: false, enhancedHue: true, colorTemperature: true },
|
|
594
|
+
options: {
|
|
595
|
+
executeIfOff: false,
|
|
596
|
+
},
|
|
597
|
+
numberOfPrimaries: null,
|
|
598
|
+
currentX,
|
|
599
|
+
currentY,
|
|
600
|
+
currentHue: Math.round((enhancedCurrentHue / 65535) * 254),
|
|
601
|
+
enhancedCurrentHue,
|
|
602
|
+
currentSaturation,
|
|
603
|
+
colorTemperatureMireds,
|
|
604
|
+
colorTempPhysicalMinMireds,
|
|
605
|
+
colorTempPhysicalMaxMireds,
|
|
606
|
+
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
607
|
+
startUpColorTemperatureMireds: null,
|
|
608
|
+
remainingTime: 0,
|
|
609
|
+
});
|
|
610
|
+
return this;
|
|
611
|
+
}
|
|
589
612
|
createXyColorControlClusterServer(currentX = 0, currentY = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
590
613
|
this.behaviors.require(MatterbridgeColorControlServer.with(ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature), {
|
|
591
614
|
colorMode: ColorControl.ColorMode.CurrentXAndCurrentY,
|
|
@@ -645,8 +668,8 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
645
668
|
return this;
|
|
646
669
|
}
|
|
647
670
|
async configureColorControlMode(colorMode) {
|
|
648
|
-
if (isValidNumber(colorMode, ColorControl.
|
|
649
|
-
await this.setAttribute(ColorControl.Cluster.id, 'colorMode', colorMode, this.log);
|
|
671
|
+
if (isValidNumber(colorMode, ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation, ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation)) {
|
|
672
|
+
await this.setAttribute(ColorControl.Cluster.id, 'colorMode', colorMode === ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation ? ColorControl.ColorMode.CurrentHueAndCurrentSaturation : colorMode, this.log);
|
|
650
673
|
await this.setAttribute(ColorControl.Cluster.id, 'enhancedColorMode', colorMode, this.log);
|
|
651
674
|
}
|
|
652
675
|
}
|
|
@@ -1,8 +1,76 @@
|
|
|
1
1
|
import { rmSync } from 'node:fs';
|
|
2
2
|
import { inspect } from 'node:util';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { jest } from '@jest/globals';
|
|
3
5
|
import { DeviceTypeId, Endpoint, Environment, ServerNode, ServerNodeStore, VendorId, LogFormat as MatterLogFormat, LogLevel as MatterLogLevel, Lifecycle } from '@matter/main';
|
|
4
6
|
import { AggregatorEndpoint, RootEndpoint } from '@matter/main/endpoints';
|
|
5
7
|
import { MdnsService } from '@matter/main/protocol';
|
|
8
|
+
import { AnsiLogger } from 'node-ansi-logger';
|
|
9
|
+
export let loggerLogSpy;
|
|
10
|
+
export let consoleLogSpy;
|
|
11
|
+
export let consoleDebugSpy;
|
|
12
|
+
export let consoleInfoSpy;
|
|
13
|
+
export let consoleWarnSpy;
|
|
14
|
+
export let consoleErrorSpy;
|
|
15
|
+
export function setupTest(name, debug = false) {
|
|
16
|
+
expect(name).toBeDefined();
|
|
17
|
+
expect(typeof name).toBe('string');
|
|
18
|
+
expect(name.length).toBeGreaterThanOrEqual(4);
|
|
19
|
+
rmSync(path.join('jest', name), { recursive: true, force: true });
|
|
20
|
+
if (debug) {
|
|
21
|
+
loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log');
|
|
22
|
+
consoleLogSpy = jest.spyOn(console, 'log');
|
|
23
|
+
consoleDebugSpy = jest.spyOn(console, 'debug');
|
|
24
|
+
consoleInfoSpy = jest.spyOn(console, 'info');
|
|
25
|
+
consoleWarnSpy = jest.spyOn(console, 'warn');
|
|
26
|
+
consoleErrorSpy = jest.spyOn(console, 'error');
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log').mockImplementation(() => { });
|
|
30
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
31
|
+
consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
|
|
32
|
+
consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(() => { });
|
|
33
|
+
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
34
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function setDebug(debug) {
|
|
38
|
+
if (debug) {
|
|
39
|
+
loggerLogSpy.mockRestore();
|
|
40
|
+
consoleLogSpy.mockRestore();
|
|
41
|
+
consoleDebugSpy.mockRestore();
|
|
42
|
+
consoleInfoSpy.mockRestore();
|
|
43
|
+
consoleWarnSpy.mockRestore();
|
|
44
|
+
consoleErrorSpy.mockRestore();
|
|
45
|
+
loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log');
|
|
46
|
+
consoleLogSpy = jest.spyOn(console, 'log');
|
|
47
|
+
consoleDebugSpy = jest.spyOn(console, 'debug');
|
|
48
|
+
consoleInfoSpy = jest.spyOn(console, 'info');
|
|
49
|
+
consoleWarnSpy = jest.spyOn(console, 'warn');
|
|
50
|
+
consoleErrorSpy = jest.spyOn(console, 'error');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log').mockImplementation(() => { });
|
|
54
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
55
|
+
consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
|
|
56
|
+
consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(() => { });
|
|
57
|
+
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
58
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function createTestEnvironment(homeDir) {
|
|
62
|
+
expect(homeDir).toBeDefined();
|
|
63
|
+
expect(typeof homeDir).toBe('string');
|
|
64
|
+
expect(homeDir.length).toBeGreaterThanOrEqual(4);
|
|
65
|
+
rmSync(homeDir, { recursive: true, force: true });
|
|
66
|
+
const environment = Environment.default;
|
|
67
|
+
environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
68
|
+
environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
69
|
+
environment.vars.set('path.root', homeDir);
|
|
70
|
+
environment.vars.set('runtime.signals', false);
|
|
71
|
+
environment.vars.set('runtime.exitcode', false);
|
|
72
|
+
return environment;
|
|
73
|
+
}
|
|
6
74
|
export async function flushAsync(ticks = 3, microTurns = 10, pause = 100) {
|
|
7
75
|
for (let i = 0; i < ticks; i++)
|
|
8
76
|
await new Promise((resolve) => setImmediate(resolve));
|
|
@@ -46,19 +114,6 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
|
46
114
|
}
|
|
47
115
|
return all.length;
|
|
48
116
|
}
|
|
49
|
-
export function createTestEnvironment(homeDir) {
|
|
50
|
-
expect(homeDir).toBeDefined();
|
|
51
|
-
expect(typeof homeDir).toBe('string');
|
|
52
|
-
expect(homeDir.length).toBeGreaterThan(5);
|
|
53
|
-
rmSync(homeDir, { recursive: true, force: true });
|
|
54
|
-
const environment = Environment.default;
|
|
55
|
-
environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
56
|
-
environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
57
|
-
environment.vars.set('path.root', homeDir);
|
|
58
|
-
environment.vars.set('runtime.signals', false);
|
|
59
|
-
environment.vars.set('runtime.exitcode', false);
|
|
60
|
-
return environment;
|
|
61
|
-
}
|
|
62
117
|
export async function startServerNode(name, port) {
|
|
63
118
|
const server = await ServerNode.create({
|
|
64
119
|
id: name + 'ServerNode',
|
|
@@ -110,6 +165,7 @@ export async function startServerNode(name, port) {
|
|
|
110
165
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
111
166
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
112
167
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
168
|
+
await flushAsync();
|
|
113
169
|
return [server, aggregator];
|
|
114
170
|
}
|
|
115
171
|
export async function stopServerNode(server) {
|
|
@@ -124,7 +180,7 @@ export async function stopServerNode(server) {
|
|
|
124
180
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
125
181
|
await flushAsync();
|
|
126
182
|
}
|
|
127
|
-
export async function addDevice(owner, device) {
|
|
183
|
+
export async function addDevice(owner, device, pause = 10) {
|
|
128
184
|
expect(owner).toBeDefined();
|
|
129
185
|
expect(device).toBeDefined();
|
|
130
186
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
@@ -146,9 +202,10 @@ export async function addDevice(owner, device) {
|
|
|
146
202
|
expect(device.lifecycle.hasId).toBeTruthy();
|
|
147
203
|
expect(device.lifecycle.hasNumber).toBeTruthy();
|
|
148
204
|
expect(device.construction.status).toBe(Lifecycle.Status.Active);
|
|
205
|
+
await flushAsync(1, 1, pause);
|
|
149
206
|
return true;
|
|
150
207
|
}
|
|
151
|
-
export async function deleteDevice(owner, device) {
|
|
208
|
+
export async function deleteDevice(owner, device, pause = 10) {
|
|
152
209
|
expect(owner).toBeDefined();
|
|
153
210
|
expect(device).toBeDefined();
|
|
154
211
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
@@ -170,5 +227,6 @@ export async function deleteDevice(owner, device) {
|
|
|
170
227
|
expect(device.lifecycle.hasId).toBeTruthy();
|
|
171
228
|
expect(device.lifecycle.hasNumber).toBeTruthy();
|
|
172
229
|
expect(device.construction.status).toBe(Lifecycle.Status.Destroyed);
|
|
230
|
+
await flushAsync(1, 1, pause);
|
|
173
231
|
return true;
|
|
174
232
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "./static/css/main.a2f4846a.css",
|
|
4
|
-
"main.js": "./static/js/main.
|
|
4
|
+
"main.js": "./static/js/main.ee68a4ae.js",
|
|
5
5
|
"static/js/453.d855a71b.chunk.js": "./static/js/453.d855a71b.chunk.js",
|
|
6
6
|
"static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
|
|
7
7
|
"static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"static/media/roboto-greek-ext-300-normal.woff": "./static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
|
|
78
78
|
"index.html": "./index.html",
|
|
79
79
|
"main.a2f4846a.css.map": "./static/css/main.a2f4846a.css.map",
|
|
80
|
-
"main.
|
|
80
|
+
"main.ee68a4ae.js.map": "./static/js/main.ee68a4ae.js.map",
|
|
81
81
|
"453.d855a71b.chunk.js.map": "./static/js/453.d855a71b.chunk.js.map"
|
|
82
82
|
},
|
|
83
83
|
"entrypoints": [
|
|
84
84
|
"static/css/main.a2f4846a.css",
|
|
85
|
-
"static/js/main.
|
|
85
|
+
"static/js/main.ee68a4ae.js"
|
|
86
86
|
]
|
|
87
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.ee68a4ae.js"></script><link href="./static/css/main.a2f4846a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|