matterbridge 3.3.3-dev-20251015-19d1be8 → 3.3.3-dev-20251015-706d832
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/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') {
|
|
@@ -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
|
+
}
|