matterbridge 3.3.3-dev-20251015-19d1be8 → 3.3.3-dev-20251016-70a1c5b
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 +4 -0
- package/README-MACOS-PLIST.md +3 -3
- package/dist/cliHistory.js +5 -2
- package/dist/frontend.js +87 -39
- package/dist/matterbridge.js +5 -3
- package/dist/matterbridgeEndpoint.js +38 -55
- package/dist/matterbridgeEndpointHelpers.js +95 -1
- package/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_mdi.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -29,6 +29,10 @@ Advantages:
|
|
|
29
29
|
|
|
30
30
|
- [thread]: Added timestamp to WorkerMessage.
|
|
31
31
|
- [macOS]: Added the [plist configuration guide](README-MACOS-PLIST.md).
|
|
32
|
+
- [frontend]: Added download diagnostic and download history to Download menu.
|
|
33
|
+
- [frontend]: Added icon to open the cpu and memory usage in System Information panel.
|
|
34
|
+
- [thermostat]: Added thermostatRunningState attribute. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge/pull/410).
|
|
35
|
+
- [ElectricalPowerMeasurement]: Added createApparentElectricalPowerMeasurementClusterServer cluster helper. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge/pull/411).
|
|
32
36
|
|
|
33
37
|
### Changed
|
|
34
38
|
|
package/README-MACOS-PLIST.md
CHANGED
|
@@ -195,12 +195,12 @@ sudo launchctl disable system/matterbridge
|
|
|
195
195
|
sudo tail -n 1000 -f /var/log/matterbridge.log /var/log/matterbridge.err
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
-
###
|
|
198
|
+
### Optional: automatically rotate logs (every 5 days or at 100 MB, keep 5 compressed backups)
|
|
199
199
|
|
|
200
200
|
```bash
|
|
201
201
|
sudo tee /etc/newsyslog.d/matterbridge.conf <<'EOF'
|
|
202
|
-
/var/log/matterbridge.log root:wheel 640 5 102400 5
|
|
203
|
-
/var/log/matterbridge.err root:wheel 640 5 102400 5
|
|
202
|
+
/var/log/matterbridge.log root:wheel 640 5 102400 5 ZC
|
|
203
|
+
/var/log/matterbridge.err root:wheel 640 5 102400 5 ZC
|
|
204
204
|
EOF
|
|
205
205
|
sudo newsyslog -v
|
|
206
206
|
```
|
package/dist/cliHistory.js
CHANGED
|
@@ -2,6 +2,7 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
2
2
|
console.log('\u001B[32mCli history loaded.\u001B[40;0m');
|
|
3
3
|
import { writeFileSync } from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
5
6
|
export const historySize = 2880;
|
|
6
7
|
export let historyIndex = 0;
|
|
7
8
|
export function setHistoryIndex(index) {
|
|
@@ -32,6 +33,7 @@ export const history = Array.from({ length: historySize }, () => ({
|
|
|
32
33
|
}));
|
|
33
34
|
export function generateHistoryPage(options = {}) {
|
|
34
35
|
const pageTitle = options.pageTitle ?? 'Matterbridge CPU & Memory History';
|
|
36
|
+
const hostname = options.hostname ?? os.hostname();
|
|
35
37
|
const outputPath = path.resolve(options.outputPath ?? path.join(process.cwd(), 'history.html'));
|
|
36
38
|
const bufferLength = history.length;
|
|
37
39
|
if (bufferLength === 0) {
|
|
@@ -101,7 +103,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
101
103
|
}
|
|
102
104
|
h1 {
|
|
103
105
|
margin-top: 0;
|
|
104
|
-
font-size: clamp(1.
|
|
106
|
+
font-size: clamp(1.4rem, 1.8vw, 2.0rem);
|
|
105
107
|
font-weight: 700;
|
|
106
108
|
color: var(--accent);
|
|
107
109
|
}
|
|
@@ -231,7 +233,8 @@ export function generateHistoryPage(options = {}) {
|
|
|
231
233
|
<div class="container">
|
|
232
234
|
<header>
|
|
233
235
|
<h1>${escapeHtml(pageTitle)}</h1>
|
|
234
|
-
<p>
|
|
236
|
+
<p>Hostname: ${escapeHtml(hostname)}</p>
|
|
237
|
+
<p>Generated: ${new Date().toLocaleString()}</p>
|
|
235
238
|
</header>
|
|
236
239
|
|
|
237
240
|
<section class="card">
|
package/dist/frontend.js
CHANGED
|
@@ -405,41 +405,7 @@ export class Frontend extends EventEmitter {
|
|
|
405
405
|
});
|
|
406
406
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
407
407
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
408
|
-
|
|
409
|
-
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
410
|
-
if (this.matterbridge.serverNode)
|
|
411
|
-
serverNodes.push(this.matterbridge.serverNode);
|
|
412
|
-
}
|
|
413
|
-
else if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
414
|
-
for (const plugin of this.matterbridge.plugins.array()) {
|
|
415
|
-
if (plugin.serverNode)
|
|
416
|
-
serverNodes.push(plugin.serverNode);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
for (const device of this.matterbridge.devices.array()) {
|
|
420
|
-
if (device.serverNode)
|
|
421
|
-
serverNodes.push(device.serverNode);
|
|
422
|
-
}
|
|
423
|
-
const fs = await import('node:fs');
|
|
424
|
-
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE)))
|
|
425
|
-
fs.unlinkSync(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE));
|
|
426
|
-
const diagnosticDestination = LogDestination({ name: 'diagnostic', level: MatterLogLevel.INFO, format: MatterLogFormat.formats.plain });
|
|
427
|
-
diagnosticDestination.write = async (text, _message) => {
|
|
428
|
-
await fs.promises.appendFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE), text + '\n', { encoding: 'utf8' });
|
|
429
|
-
};
|
|
430
|
-
Logger.destinations.diagnostic = diagnosticDestination;
|
|
431
|
-
if (!diagnosticDestination.context) {
|
|
432
|
-
diagnosticDestination.context = Diagnostic.Context();
|
|
433
|
-
}
|
|
434
|
-
diagnosticDestination.context.run(() => diagnosticDestination.add(Diagnostic.message({
|
|
435
|
-
now: Time.now(),
|
|
436
|
-
facility: 'Server nodes:',
|
|
437
|
-
level: MatterLogLevel.INFO,
|
|
438
|
-
prefix: Logger.nestingLevel ? '⎸'.padEnd(Logger.nestingLevel * 2) : '',
|
|
439
|
-
values: [...serverNodes],
|
|
440
|
-
})));
|
|
441
|
-
delete Logger.destinations.diagnostic;
|
|
442
|
-
await wait(500);
|
|
408
|
+
await this.generateDiagnostic();
|
|
443
409
|
try {
|
|
444
410
|
const fs = await import('node:fs');
|
|
445
411
|
const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE), 'utf8');
|
|
@@ -451,6 +417,26 @@ export class Frontend extends EventEmitter {
|
|
|
451
417
|
res.status(500).send('Error reading diagnostic log file.');
|
|
452
418
|
}
|
|
453
419
|
});
|
|
420
|
+
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
421
|
+
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
422
|
+
await this.generateDiagnostic();
|
|
423
|
+
try {
|
|
424
|
+
const fs = await import('node:fs');
|
|
425
|
+
await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE), fs.constants.F_OK);
|
|
426
|
+
const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE), 'utf8');
|
|
427
|
+
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
431
|
+
}
|
|
432
|
+
res.type('text/plain');
|
|
433
|
+
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
434
|
+
if (error) {
|
|
435
|
+
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
436
|
+
res.status(500).send('Error downloading the diagnostic log file');
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
});
|
|
454
440
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
455
441
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
456
442
|
try {
|
|
@@ -460,8 +446,28 @@ export class Frontend extends EventEmitter {
|
|
|
460
446
|
res.send(data);
|
|
461
447
|
}
|
|
462
448
|
catch (error) {
|
|
463
|
-
this.log.error(`Error reading history
|
|
464
|
-
res.status(500).send('Error reading history
|
|
449
|
+
this.log.error(`Error in /api/viewhistory reading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
450
|
+
res.status(500).send('Error reading history file.');
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
454
|
+
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
455
|
+
try {
|
|
456
|
+
const fs = await import('node:fs');
|
|
457
|
+
await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), fs.constants.F_OK);
|
|
458
|
+
const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), 'utf8');
|
|
459
|
+
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
460
|
+
res.type('text/plain');
|
|
461
|
+
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
462
|
+
if (error) {
|
|
463
|
+
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
464
|
+
res.status(500).send('Error downloading history file');
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
this.log.error(`Error in /api/downloadhistory reading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
470
|
+
res.status(500).send('Error reading history file.');
|
|
465
471
|
}
|
|
466
472
|
});
|
|
467
473
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
@@ -999,6 +1005,44 @@ export class Frontend extends EventEmitter {
|
|
|
999
1005
|
});
|
|
1000
1006
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, number: endpoint.number, id: endpoint.id, deviceTypes, clusters };
|
|
1001
1007
|
}
|
|
1008
|
+
async generateDiagnostic() {
|
|
1009
|
+
this.log.debug('Generating diagnostic...');
|
|
1010
|
+
const serverNodes = [];
|
|
1011
|
+
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1012
|
+
if (this.matterbridge.serverNode)
|
|
1013
|
+
serverNodes.push(this.matterbridge.serverNode);
|
|
1014
|
+
}
|
|
1015
|
+
else if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
1016
|
+
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1017
|
+
if (plugin.serverNode)
|
|
1018
|
+
serverNodes.push(plugin.serverNode);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
for (const device of this.matterbridge.devices.array()) {
|
|
1022
|
+
if (device.serverNode)
|
|
1023
|
+
serverNodes.push(device.serverNode);
|
|
1024
|
+
}
|
|
1025
|
+
const fs = await import('node:fs');
|
|
1026
|
+
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE)))
|
|
1027
|
+
fs.unlinkSync(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE));
|
|
1028
|
+
const diagnosticDestination = LogDestination({ name: 'diagnostic', level: MatterLogLevel.INFO, format: MatterLogFormat.formats.plain });
|
|
1029
|
+
diagnosticDestination.write = async (text, _message) => {
|
|
1030
|
+
await fs.promises.appendFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_DIAGNOSTIC_FILE), text + '\n', { encoding: 'utf8' });
|
|
1031
|
+
};
|
|
1032
|
+
Logger.destinations.diagnostic = diagnosticDestination;
|
|
1033
|
+
if (!diagnosticDestination.context) {
|
|
1034
|
+
diagnosticDestination.context = Diagnostic.Context();
|
|
1035
|
+
}
|
|
1036
|
+
diagnosticDestination.context.run(() => diagnosticDestination.add(Diagnostic.message({
|
|
1037
|
+
now: Time.now(),
|
|
1038
|
+
facility: 'Server nodes:',
|
|
1039
|
+
level: MatterLogLevel.INFO,
|
|
1040
|
+
prefix: Logger.nestingLevel ? '⎸'.padEnd(Logger.nestingLevel * 2) : '',
|
|
1041
|
+
values: [...serverNodes],
|
|
1042
|
+
})));
|
|
1043
|
+
delete Logger.destinations.diagnostic;
|
|
1044
|
+
await wait(500);
|
|
1045
|
+
}
|
|
1002
1046
|
async wsMessageHandler(client, message) {
|
|
1003
1047
|
let data;
|
|
1004
1048
|
const sendResponse = (data) => {
|
|
@@ -1286,8 +1330,12 @@ export class Frontend extends EventEmitter {
|
|
|
1286
1330
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
1287
1331
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1288
1332
|
}
|
|
1289
|
-
else if (data.method === '/api/
|
|
1290
|
-
generateHistoryPage({ outputPath: path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), pageTitle: `Matterbridge
|
|
1333
|
+
else if (data.method === '/api/viewhistorypage') {
|
|
1334
|
+
generateHistoryPage({ outputPath: path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), pageTitle: `Matterbridge Cpu & Memory History` });
|
|
1335
|
+
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1336
|
+
}
|
|
1337
|
+
else if (data.method === '/api/downloadhistorypage') {
|
|
1338
|
+
generateHistoryPage({ outputPath: path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), pageTitle: `Matterbridge Cpu & Memory History` });
|
|
1291
1339
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1292
1340
|
}
|
|
1293
1341
|
else if (data.method === '/api/matter') {
|
package/dist/matterbridge.js
CHANGED
|
@@ -14,7 +14,7 @@ import { BasicInformationServer } from '@matter/main/behaviors/basic-information
|
|
|
14
14
|
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
15
15
|
import { copyDirectory } from './utils/copyDirectory.js';
|
|
16
16
|
import { createDirectory } from './utils/createDirectory.js';
|
|
17
|
-
import { isValidString, parseVersionString, isValidNumber } from './utils/isvalid.js';
|
|
17
|
+
import { isValidString, parseVersionString, isValidNumber, isValidObject } from './utils/isvalid.js';
|
|
18
18
|
import { formatMemoryUsage, formatOsUpTime } from './utils/network.js';
|
|
19
19
|
import { withTimeout, waiter, wait } from './utils/wait.js';
|
|
20
20
|
import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from './matterbridgeTypes.js';
|
|
@@ -1737,6 +1737,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1737
1737
|
{ cluster: 'Thermostat', attribute: 'occupiedCoolingSetpoint' },
|
|
1738
1738
|
{ cluster: 'Thermostat', attribute: 'occupiedHeatingSetpoint' },
|
|
1739
1739
|
{ cluster: 'Thermostat', attribute: 'systemMode' },
|
|
1740
|
+
{ cluster: 'Thermostat', attribute: 'thermostatRunningMode' },
|
|
1741
|
+
{ cluster: 'Thermostat', attribute: 'thermostatRunningState' },
|
|
1740
1742
|
{ cluster: 'WindowCovering', attribute: 'operationalStatus' },
|
|
1741
1743
|
{ cluster: 'WindowCovering', attribute: 'currentPositionLiftPercent100ths' },
|
|
1742
1744
|
{ cluster: 'DoorLock', attribute: 'lockState' },
|
|
@@ -1766,7 +1768,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1766
1768
|
if (device.hasAttributeServer(sub.cluster, sub.attribute)) {
|
|
1767
1769
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
1768
1770
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
1769
|
-
this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
|
|
1771
|
+
this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
|
|
1770
1772
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
1771
1773
|
});
|
|
1772
1774
|
}
|
|
@@ -1774,7 +1776,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1774
1776
|
if (child.hasAttributeServer(sub.cluster, sub.attribute)) {
|
|
1775
1777
|
this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
1776
1778
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
1777
|
-
this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
|
|
1779
|
+
this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
|
|
1778
1780
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
1779
1781
|
});
|
|
1780
1782
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, UINT16_MAX, UINT32_MAX, VendorId } from '@matter/main';
|
|
2
|
-
import { getClusterNameById
|
|
2
|
+
import { getClusterNameById } from '@matter/main/types';
|
|
3
3
|
import { Descriptor } from '@matter/main/clusters/descriptor';
|
|
4
4
|
import { PowerSource } from '@matter/main/clusters/power-source';
|
|
5
5
|
import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
|
|
@@ -60,7 +60,7 @@ import { ThermostatUserInterfaceConfigurationServer } from '@matter/main/behavio
|
|
|
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
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
|
-
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';
|
|
63
|
+
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getApparentElectricalPowerMeasurementClusterServer, 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";
|
|
66
66
|
mode = undefined;
|
|
@@ -782,8 +782,17 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
782
782
|
this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : [])), {
|
|
783
783
|
localTemperature: localTemperature * 100,
|
|
784
784
|
...(outdoorTemperature !== undefined ? { outdoorTemperature: outdoorTemperature !== null ? outdoorTemperature * 100 : outdoorTemperature } : {}),
|
|
785
|
-
systemMode: Thermostat.SystemMode.Auto,
|
|
786
785
|
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingAndHeating,
|
|
786
|
+
systemMode: Thermostat.SystemMode.Auto,
|
|
787
|
+
thermostatRunningState: {
|
|
788
|
+
heat: false,
|
|
789
|
+
cool: false,
|
|
790
|
+
fan: false,
|
|
791
|
+
heatStage2: false,
|
|
792
|
+
coolStage2: false,
|
|
793
|
+
fanStage2: false,
|
|
794
|
+
fanStage3: false,
|
|
795
|
+
},
|
|
787
796
|
occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
|
|
788
797
|
minHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
789
798
|
maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
@@ -806,8 +815,17 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
806
815
|
this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Heating, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : [])), {
|
|
807
816
|
localTemperature: localTemperature * 100,
|
|
808
817
|
...(outdoorTemperature !== undefined ? { outdoorTemperature: outdoorTemperature !== null ? outdoorTemperature * 100 : outdoorTemperature } : {}),
|
|
809
|
-
systemMode: Thermostat.SystemMode.Heat,
|
|
810
818
|
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.HeatingOnly,
|
|
819
|
+
systemMode: Thermostat.SystemMode.Heat,
|
|
820
|
+
thermostatRunningState: {
|
|
821
|
+
heat: false,
|
|
822
|
+
cool: false,
|
|
823
|
+
fan: false,
|
|
824
|
+
heatStage2: false,
|
|
825
|
+
coolStage2: false,
|
|
826
|
+
fanStage2: false,
|
|
827
|
+
fanStage3: false,
|
|
828
|
+
},
|
|
811
829
|
occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
|
|
812
830
|
minHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
813
831
|
maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
@@ -822,8 +840,17 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
822
840
|
this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Cooling, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : [])), {
|
|
823
841
|
localTemperature: localTemperature * 100,
|
|
824
842
|
...(outdoorTemperature !== undefined ? { outdoorTemperature: outdoorTemperature !== null ? outdoorTemperature * 100 : outdoorTemperature } : {}),
|
|
825
|
-
systemMode: Thermostat.SystemMode.Cool,
|
|
826
843
|
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingOnly,
|
|
844
|
+
systemMode: Thermostat.SystemMode.Cool,
|
|
845
|
+
thermostatRunningState: {
|
|
846
|
+
heat: false,
|
|
847
|
+
cool: false,
|
|
848
|
+
fan: false,
|
|
849
|
+
heatStage2: false,
|
|
850
|
+
coolStage2: false,
|
|
851
|
+
fanStage2: false,
|
|
852
|
+
fanStage3: false,
|
|
853
|
+
},
|
|
827
854
|
occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
|
|
828
855
|
minCoolSetpointLimit: minCoolSetpointLimit * 100,
|
|
829
856
|
maxCoolSetpointLimit: maxCoolSetpointLimit * 100,
|
|
@@ -1170,59 +1197,15 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1170
1197
|
return this;
|
|
1171
1198
|
}
|
|
1172
1199
|
createDefaultElectricalEnergyMeasurementClusterServer(energyImported = null, energyExported = null) {
|
|
1173
|
-
this.behaviors.require(ElectricalEnergyMeasurementServer.with(ElectricalEnergyMeasurement.Feature.ImportedEnergy, ElectricalEnergyMeasurement.Feature.ExportedEnergy, ElectricalEnergyMeasurement.Feature.CumulativeEnergy),
|
|
1174
|
-
accuracy: {
|
|
1175
|
-
measurementType: MeasurementType.ElectricalEnergy,
|
|
1176
|
-
measured: true,
|
|
1177
|
-
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
1178
|
-
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
1179
|
-
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
1180
|
-
},
|
|
1181
|
-
cumulativeEnergyReset: null,
|
|
1182
|
-
cumulativeEnergyImported: energyImported !== null && energyImported >= 0 ? { energy: energyImported } : null,
|
|
1183
|
-
cumulativeEnergyExported: energyExported !== null && energyExported >= 0 ? { energy: energyExported } : null,
|
|
1184
|
-
});
|
|
1200
|
+
this.behaviors.require(ElectricalEnergyMeasurementServer.with(ElectricalEnergyMeasurement.Feature.ImportedEnergy, ElectricalEnergyMeasurement.Feature.ExportedEnergy, ElectricalEnergyMeasurement.Feature.CumulativeEnergy), getDefaultElectricalEnergyMeasurementClusterServer(energyImported, energyExported));
|
|
1185
1201
|
return this;
|
|
1186
1202
|
}
|
|
1187
1203
|
createDefaultElectricalPowerMeasurementClusterServer(voltage = null, current = null, power = null, frequency = null) {
|
|
1188
|
-
this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent),
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
measurementType: MeasurementType.Voltage,
|
|
1194
|
-
measured: true,
|
|
1195
|
-
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
1196
|
-
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
1197
|
-
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
1198
|
-
},
|
|
1199
|
-
{
|
|
1200
|
-
measurementType: MeasurementType.ActiveCurrent,
|
|
1201
|
-
measured: true,
|
|
1202
|
-
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
1203
|
-
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
1204
|
-
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
measurementType: MeasurementType.ActivePower,
|
|
1208
|
-
measured: true,
|
|
1209
|
-
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
1210
|
-
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
1211
|
-
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
1212
|
-
},
|
|
1213
|
-
{
|
|
1214
|
-
measurementType: MeasurementType.Frequency,
|
|
1215
|
-
measured: true,
|
|
1216
|
-
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
1217
|
-
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
1218
|
-
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
1219
|
-
},
|
|
1220
|
-
],
|
|
1221
|
-
voltage: voltage,
|
|
1222
|
-
activeCurrent: current,
|
|
1223
|
-
activePower: power,
|
|
1224
|
-
frequency: frequency,
|
|
1225
|
-
});
|
|
1204
|
+
this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getDefaultElectricalPowerMeasurementClusterServer(voltage, current, power, frequency));
|
|
1205
|
+
return this;
|
|
1206
|
+
}
|
|
1207
|
+
createApparentElectricalPowerMeasurementClusterServer(voltage = null, apparentCurrent = null, apparentPower = null, frequency = null) {
|
|
1208
|
+
this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getApparentElectricalPowerMeasurementClusterServer(voltage, apparentCurrent, apparentPower, frequency));
|
|
1226
1209
|
return this;
|
|
1227
1210
|
}
|
|
1228
1211
|
createDefaultTemperatureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { BLUE, CYAN, db, debugStringify, er, hk, or, YELLOW, zb } from 'node-ansi-logger';
|
|
3
3
|
import { Lifecycle } from '@matter/main';
|
|
4
|
-
import { getClusterNameById } from '@matter/main/types';
|
|
4
|
+
import { getClusterNameById, MeasurementType } from '@matter/main/types';
|
|
5
5
|
import { PowerSource } from '@matter/main/clusters/power-source';
|
|
6
6
|
import { UserLabel } from '@matter/main/clusters/user-label';
|
|
7
7
|
import { FixedLabel } from '@matter/main/clusters/fixed-label';
|
|
@@ -601,3 +601,97 @@ export function getDefaultOccupancySensingClusterServer(occupied = false, holdTi
|
|
|
601
601
|
holdTimeLimits: { holdTimeMin, holdTimeMax, holdTimeDefault: holdTime },
|
|
602
602
|
});
|
|
603
603
|
}
|
|
604
|
+
export function getDefaultElectricalEnergyMeasurementClusterServer(energyImported = null, energyExported = null) {
|
|
605
|
+
return optionsFor(ElectricalEnergyMeasurementServer.with(ElectricalEnergyMeasurement.Feature.ImportedEnergy, ElectricalEnergyMeasurement.Feature.ExportedEnergy, ElectricalEnergyMeasurement.Feature.CumulativeEnergy), {
|
|
606
|
+
accuracy: {
|
|
607
|
+
measurementType: MeasurementType.ElectricalEnergy,
|
|
608
|
+
measured: true,
|
|
609
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
610
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
611
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
612
|
+
},
|
|
613
|
+
cumulativeEnergyReset: null,
|
|
614
|
+
cumulativeEnergyImported: energyImported !== null && energyImported >= 0 ? { energy: energyImported } : null,
|
|
615
|
+
cumulativeEnergyExported: energyExported !== null && energyExported >= 0 ? { energy: energyExported } : null,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
export function getDefaultElectricalPowerMeasurementClusterServer(voltage = null, current = null, power = null, frequency = null) {
|
|
619
|
+
return optionsFor(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), {
|
|
620
|
+
powerMode: ElectricalPowerMeasurement.PowerMode.Ac,
|
|
621
|
+
numberOfMeasurementTypes: 4,
|
|
622
|
+
accuracy: [
|
|
623
|
+
{
|
|
624
|
+
measurementType: MeasurementType.Voltage,
|
|
625
|
+
measured: true,
|
|
626
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
627
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
628
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
measurementType: MeasurementType.ActiveCurrent,
|
|
632
|
+
measured: true,
|
|
633
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
634
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
635
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
measurementType: MeasurementType.ActivePower,
|
|
639
|
+
measured: true,
|
|
640
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
641
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
642
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
measurementType: MeasurementType.Frequency,
|
|
646
|
+
measured: true,
|
|
647
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
648
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
649
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
voltage: voltage,
|
|
653
|
+
activeCurrent: current,
|
|
654
|
+
activePower: power,
|
|
655
|
+
frequency: frequency,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
export function getApparentElectricalPowerMeasurementClusterServer(voltage = null, apparentCurrent = null, apparentPower = null, frequency = null) {
|
|
659
|
+
return optionsFor(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), {
|
|
660
|
+
powerMode: ElectricalPowerMeasurement.PowerMode.Ac,
|
|
661
|
+
numberOfMeasurementTypes: 4,
|
|
662
|
+
accuracy: [
|
|
663
|
+
{
|
|
664
|
+
measurementType: MeasurementType.Voltage,
|
|
665
|
+
measured: true,
|
|
666
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
667
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
668
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
measurementType: MeasurementType.ApparentCurrent,
|
|
672
|
+
measured: true,
|
|
673
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
674
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
675
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
measurementType: MeasurementType.ApparentPower,
|
|
679
|
+
measured: true,
|
|
680
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
681
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
682
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
measurementType: MeasurementType.Frequency,
|
|
686
|
+
measured: true,
|
|
687
|
+
minMeasuredValue: Number.MIN_SAFE_INTEGER,
|
|
688
|
+
maxMeasuredValue: Number.MAX_SAFE_INTEGER,
|
|
689
|
+
accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
|
|
690
|
+
},
|
|
691
|
+
],
|
|
692
|
+
voltage: voltage,
|
|
693
|
+
apparentCurrent: apparentCurrent,
|
|
694
|
+
apparentPower: apparentPower,
|
|
695
|
+
frequency: frequency,
|
|
696
|
+
});
|
|
697
|
+
}
|