@matterbridge/core 3.10.13-dev-20261005-49e0dbd → 3.10.13-dev-20261007-320f739
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/dist/cli.js +5 -0
- package/dist/deviceManager.d.ts +2 -1
- package/dist/deviceManager.js +29 -0
- package/dist/frontend.d.ts +1 -5
- package/dist/frontend.js +19 -300
- package/dist/matterNode.d.ts +3 -8
- package/dist/matterNode.js +25 -115
- package/dist/matterNodeHelpers.d.ts +15 -0
- package/dist/matterNodeHelpers.js +393 -0
- package/dist/matterbridge.d.ts +1 -6
- package/dist/matterbridge.js +48 -116
- package/dist/pluginManager.js +2 -1
- package/package.json +5 -5
- package/dist/backend.d.ts +0 -37
- package/dist/backend.js +0 -308
- package/dist/backendExpress.d.ts +0 -20
- package/dist/backendExpress.js +0 -453
- package/dist/backendWsServer.d.ts +0 -38
- package/dist/backendWsServer.js +0 -326
package/dist/cli.js
CHANGED
|
@@ -100,6 +100,11 @@ async function main() {
|
|
|
100
100
|
const { Matterbridge } = await import('./matterbridge.js');
|
|
101
101
|
instance = await Matterbridge.loadInstance(true);
|
|
102
102
|
log.debug(`***Matterbridge.loadInstance(true) exited`);
|
|
103
|
+
if (hasParameter('experimental')) {
|
|
104
|
+
log.debug(`***Starting Backend thread...`);
|
|
105
|
+
manager.runThread('Backend');
|
|
106
|
+
log.debug(`***Started Backend thread`);
|
|
107
|
+
}
|
|
103
108
|
if (!instance || instance.shutdown) {
|
|
104
109
|
await shutdown();
|
|
105
110
|
}
|
package/dist/deviceManager.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseDevice } from '@matterbridge/types';
|
|
1
|
+
import type { ApiDevice, BaseDevice } from '@matterbridge/types';
|
|
2
2
|
import { LogLevel } from 'node-ansi-logger';
|
|
3
3
|
import type { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
4
4
|
export declare function toBaseDevice(device: MatterbridgeEndpoint | BaseDevice): BaseDevice;
|
|
@@ -21,6 +21,7 @@ export declare class DeviceManager {
|
|
|
21
21
|
private toBaseDevice;
|
|
22
22
|
array(): MatterbridgeEndpoint[];
|
|
23
23
|
baseArray(pluginName?: string): BaseDevice[];
|
|
24
|
+
apiDeviceArray(pluginName?: string): ApiDevice[];
|
|
24
25
|
[Symbol.iterator](): IterableIterator<MatterbridgeEndpoint>;
|
|
25
26
|
forEach(callback: (device: MatterbridgeEndpoint) => Promise<void>): Promise<void>;
|
|
26
27
|
set logLevel(logLevel: LogLevel);
|
package/dist/deviceManager.js
CHANGED
|
@@ -3,6 +3,7 @@ import { dev } from '@matterbridge/types';
|
|
|
3
3
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
4
4
|
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
5
5
|
import { AnsiLogger, BLUE, CYAN, db, debugStringify, er } from 'node-ansi-logger';
|
|
6
|
+
import { getBatteryLevel, getClusterTextFromDevice, getPowerSource, getReachability, getServerNodeData } from './matterNodeHelpers.js';
|
|
6
7
|
logModuleLoaded('Device Manager');
|
|
7
8
|
export function toBaseDevice(device) {
|
|
8
9
|
return {
|
|
@@ -87,6 +88,9 @@ export class DeviceManager {
|
|
|
87
88
|
case 'devices_basearray':
|
|
88
89
|
this.server.respond({ ...msg, result: { devices: this.baseArray(msg.params.pluginName) } });
|
|
89
90
|
break;
|
|
91
|
+
case 'devices_apidevicearray':
|
|
92
|
+
this.server.respond({ ...msg, result: { devices: this.apiDeviceArray(msg.params.pluginName) } });
|
|
93
|
+
break;
|
|
90
94
|
default:
|
|
91
95
|
if (this.verbose)
|
|
92
96
|
this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
@@ -138,6 +142,31 @@ export class DeviceManager {
|
|
|
138
142
|
}
|
|
139
143
|
return devices;
|
|
140
144
|
}
|
|
145
|
+
apiDeviceArray(pluginName) {
|
|
146
|
+
const devices = [];
|
|
147
|
+
for (const device of this._devices.values()) {
|
|
148
|
+
if (pluginName && pluginName !== device.plugin)
|
|
149
|
+
continue;
|
|
150
|
+
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
151
|
+
continue;
|
|
152
|
+
devices.push({
|
|
153
|
+
pluginName: device.plugin,
|
|
154
|
+
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
155
|
+
endpoint: device.number,
|
|
156
|
+
name: device.deviceName,
|
|
157
|
+
serial: device.serialNumber,
|
|
158
|
+
productUrl: device.productUrl,
|
|
159
|
+
configUrl: device.configUrl,
|
|
160
|
+
uniqueId: device.uniqueId,
|
|
161
|
+
reachable: getReachability(device),
|
|
162
|
+
powerSource: getPowerSource(device),
|
|
163
|
+
batteryLevel: getBatteryLevel(device),
|
|
164
|
+
matter: device.mode === 'server' && device.serverNode ? getServerNodeData(device.serverNode) : undefined,
|
|
165
|
+
cluster: getClusterTextFromDevice(device),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return devices;
|
|
169
|
+
}
|
|
141
170
|
[Symbol.iterator]() {
|
|
142
171
|
return this._devices.values();
|
|
143
172
|
}
|
package/dist/frontend.d.ts
CHANGED
|
@@ -43,13 +43,9 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
|
|
|
43
43
|
start(port?: number): Promise<void>;
|
|
44
44
|
stop(): Promise<void>;
|
|
45
45
|
getApiSettings(): ApiSettings;
|
|
46
|
-
private getReachability;
|
|
47
|
-
private getPowerSource;
|
|
48
|
-
private getBatteryLevel;
|
|
49
|
-
private getClusterTextFromDevice;
|
|
50
46
|
getApiPlugins(): ApiPlugin[];
|
|
51
47
|
getApiDevices(pluginName?: string): ApiDevice[];
|
|
52
|
-
|
|
48
|
+
getApiCluster(pluginName: string, endpointNumber: number, serialNumber?: string, uniqueId?: string): ApiClusters | undefined;
|
|
53
49
|
generateDiagnostic(): Promise<void>;
|
|
54
50
|
private wsMessageHandler;
|
|
55
51
|
wssSendLogMessage(level: string, time: string, name: string, message: string): void;
|
package/dist/frontend.js
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import EventEmitter from 'node:events';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { Diagnostic,
|
|
4
|
+
import { Diagnostic, LogDestination, LogFormat as MatterLogFormat, Logger, LogLevel as MatterLogLevel } from '@matter/general';
|
|
5
5
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/protocol';
|
|
6
|
-
import { getClusterNameById } from '@matter/types/cluster';
|
|
7
|
-
import { AirQuality } from '@matter/types/clusters/air-quality';
|
|
8
|
-
import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
|
|
9
|
-
import { ClosureControl } from '@matter/types/clusters/closure-control';
|
|
10
|
-
import { DeviceEnergyManagement } from '@matter/types/clusters/device-energy-management';
|
|
11
|
-
import { FanControl } from '@matter/types/clusters/fan-control';
|
|
12
|
-
import { OperationalState } from '@matter/types/clusters/operational-state';
|
|
13
|
-
import { PowerSource } from '@matter/types/clusters/power-source';
|
|
14
|
-
import { RvcOperationalState } from '@matter/types/clusters/rvc-operational-state';
|
|
15
|
-
import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
|
|
16
|
-
import { SoilMeasurement } from '@matter/types/clusters/soil-measurement';
|
|
17
|
-
import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
|
|
18
6
|
import { CommissioningOptions } from '@matter/types/commissioning';
|
|
19
7
|
import { FabricIndex } from '@matter/types/datatype';
|
|
20
|
-
import { ThreeLevelAuto } from '@matter/types/globals';
|
|
21
8
|
import { BroadcastServer } from '@matterbridge/thread/server';
|
|
22
9
|
import { MATTER_LOGGER_FILE, MATTER_STORAGE_DIR, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
|
|
23
|
-
import { isBun } from '@matterbridge/utils/bun';
|
|
10
|
+
import { isBun, memoryFootprint } from '@matterbridge/utils/bun';
|
|
24
11
|
import { getParameter, hasParameter } from '@matterbridge/utils/cli';
|
|
25
12
|
import { writeDiagnostic } from '@matterbridge/utils/diagnostic';
|
|
26
|
-
import { getEnumDescription } from '@matterbridge/utils/enum';
|
|
27
13
|
import { getErrorMessage, inspectError, logError } from '@matterbridge/utils/error';
|
|
28
14
|
import { formatBytes, formatPercent, formatUptime } from '@matterbridge/utils/format';
|
|
29
15
|
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
@@ -32,7 +18,8 @@ import { fireAndForget, wait, withTimeout } from '@matterbridge/utils/wait';
|
|
|
32
18
|
import { AnsiLogger, bgHex, CYAN, db, debugStringify, er, GREEN, nf, nt, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from 'node-ansi-logger';
|
|
33
19
|
import { cliEmitter, lastOsCpuUsage, lastProcessCpuUsage } from './cliEmitter.js';
|
|
34
20
|
import { generateHistoryPage } from './cliHistory.js';
|
|
35
|
-
import { capitalizeFirstLetter
|
|
21
|
+
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
22
|
+
import { deleteAdvertisingNode, getBatteryLevel, getClusterTextFromDevice, getPowerSource, getReachability, getServerNodeData, setAdvertisingNode } from './matterNodeHelpers.js';
|
|
36
23
|
logModuleLoaded('Frontend');
|
|
37
24
|
export class Frontend extends EventEmitter {
|
|
38
25
|
matterbridge;
|
|
@@ -944,7 +931,7 @@ export class Frontend extends EventEmitter {
|
|
|
944
931
|
this.matterbridge.systemInformation.processUptime = formatUptime(Math.floor(process.uptime()));
|
|
945
932
|
this.matterbridge.systemInformation.cpuUsage = formatPercent(lastOsCpuUsage);
|
|
946
933
|
this.matterbridge.systemInformation.processCpuUsage = formatPercent(lastProcessCpuUsage);
|
|
947
|
-
this.matterbridge.systemInformation.rss = formatBytes(
|
|
934
|
+
this.matterbridge.systemInformation.rss = formatBytes(memoryFootprint());
|
|
948
935
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
949
936
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
950
937
|
const info = {
|
|
@@ -991,274 +978,6 @@ export class Frontend extends EventEmitter {
|
|
|
991
978
|
};
|
|
992
979
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
993
980
|
}
|
|
994
|
-
getReachability(device) {
|
|
995
|
-
if (this.matterbridge.hasCleanupStarted)
|
|
996
|
-
return false;
|
|
997
|
-
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
998
|
-
return false;
|
|
999
|
-
if (device.hasClusterServer(BridgedDeviceBasicInformation.id))
|
|
1000
|
-
return device.getAttribute(BridgedDeviceBasicInformation, 'reachable');
|
|
1001
|
-
if (device.mode === 'server' && device.serverNode?.state.basicInformation.reachable !== undefined)
|
|
1002
|
-
return device.serverNode.state.basicInformation.reachable;
|
|
1003
|
-
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
1004
|
-
return true;
|
|
1005
|
-
return false;
|
|
1006
|
-
}
|
|
1007
|
-
getPowerSource(endpoint) {
|
|
1008
|
-
if (this.matterbridge.hasCleanupStarted)
|
|
1009
|
-
return undefined;
|
|
1010
|
-
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1011
|
-
return undefined;
|
|
1012
|
-
const powerSource = (device) => {
|
|
1013
|
-
const featureMap = device.getAttribute(PowerSource.id, 'featureMap');
|
|
1014
|
-
if (featureMap.wired) {
|
|
1015
|
-
const wiredCurrentType = device.getAttribute(PowerSource.id, 'wiredCurrentType');
|
|
1016
|
-
return ['ac', 'dc'][wiredCurrentType];
|
|
1017
|
-
}
|
|
1018
|
-
if (featureMap.battery) {
|
|
1019
|
-
const batChargeLevel = device.getAttribute(PowerSource.id, 'batChargeLevel');
|
|
1020
|
-
return ['ok', 'warning', 'critical'][batChargeLevel];
|
|
1021
|
-
}
|
|
1022
|
-
return undefined;
|
|
1023
|
-
};
|
|
1024
|
-
if (endpoint.hasClusterServer(PowerSource.id))
|
|
1025
|
-
return powerSource(endpoint);
|
|
1026
|
-
for (const child of endpoint.getChildEndpoints()) {
|
|
1027
|
-
if (child.hasClusterServer(PowerSource.id))
|
|
1028
|
-
return powerSource(child);
|
|
1029
|
-
}
|
|
1030
|
-
return undefined;
|
|
1031
|
-
}
|
|
1032
|
-
getBatteryLevel(endpoint) {
|
|
1033
|
-
if (this.matterbridge.hasCleanupStarted)
|
|
1034
|
-
return undefined;
|
|
1035
|
-
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1036
|
-
return undefined;
|
|
1037
|
-
const batteryLevel = (device) => {
|
|
1038
|
-
const featureMap = device.getAttribute(PowerSource.id, 'featureMap');
|
|
1039
|
-
if (featureMap.battery) {
|
|
1040
|
-
const batChargeLevel = device.getAttribute(PowerSource.id, 'batPercentRemaining');
|
|
1041
|
-
return isValidNumber(batChargeLevel) ? batChargeLevel / 2 : undefined;
|
|
1042
|
-
}
|
|
1043
|
-
return undefined;
|
|
1044
|
-
};
|
|
1045
|
-
if (endpoint.hasClusterServer(PowerSource.id))
|
|
1046
|
-
return batteryLevel(endpoint);
|
|
1047
|
-
for (const child of endpoint.getChildEndpoints()) {
|
|
1048
|
-
if (child.hasClusterServer(PowerSource.id))
|
|
1049
|
-
return batteryLevel(child);
|
|
1050
|
-
}
|
|
1051
|
-
return undefined;
|
|
1052
|
-
}
|
|
1053
|
-
getClusterTextFromDevice(device) {
|
|
1054
|
-
if (this.matterbridge.hasCleanupStarted)
|
|
1055
|
-
return '';
|
|
1056
|
-
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1057
|
-
return '';
|
|
1058
|
-
const getUserLabel = (device) => {
|
|
1059
|
-
const labelList = getAttribute(device, 'userLabel', 'labelList');
|
|
1060
|
-
if (labelList) {
|
|
1061
|
-
const composed = labelList.find((entry) => entry.label === 'composed');
|
|
1062
|
-
if (composed)
|
|
1063
|
-
return 'Composed: ' + composed.value;
|
|
1064
|
-
}
|
|
1065
|
-
return '';
|
|
1066
|
-
};
|
|
1067
|
-
const getFixedLabel = (device) => {
|
|
1068
|
-
const labelList = getAttribute(device, 'fixedLabel', 'labelList');
|
|
1069
|
-
if (labelList) {
|
|
1070
|
-
const composed = labelList.find((entry) => entry.label === 'composed');
|
|
1071
|
-
if (composed)
|
|
1072
|
-
return 'Composed: ' + composed.value;
|
|
1073
|
-
}
|
|
1074
|
-
return '';
|
|
1075
|
-
};
|
|
1076
|
-
let attributes = '';
|
|
1077
|
-
let supportedModes = [];
|
|
1078
|
-
const getMeasurementText = (value, scale, unit) => {
|
|
1079
|
-
if (value === null)
|
|
1080
|
-
return 'unknown';
|
|
1081
|
-
if (isValidNumber(value) || typeof value === 'bigint')
|
|
1082
|
-
return `${Number(value) / scale}${unit}`;
|
|
1083
|
-
return '';
|
|
1084
|
-
};
|
|
1085
|
-
const getEnergyText = (value) => {
|
|
1086
|
-
if (value === null)
|
|
1087
|
-
return 'unknown';
|
|
1088
|
-
if (isValidObject(value) && 'energy' in value && (isValidNumber(value.energy) || typeof value.energy === 'bigint'))
|
|
1089
|
-
return `${Number(value.energy) / 1_000_000}kWh`;
|
|
1090
|
-
return '';
|
|
1091
|
-
};
|
|
1092
|
-
const appendMeasurement = (label, value) => {
|
|
1093
|
-
if (value)
|
|
1094
|
-
attributes += `${label}: ${value} `;
|
|
1095
|
-
};
|
|
1096
|
-
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1097
|
-
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1098
|
-
return;
|
|
1099
|
-
if (clusterName === 'descriptor' && attributeName === 'clientList' && isValidArray(attributeValue, 1))
|
|
1100
|
-
attributes += `Client cluster(s): [${attributeValue.map((id) => getClusterNameById(id)).join(', ')}] `;
|
|
1101
|
-
if (clusterName === 'binding' && attributeName === 'binding' && isValidArray(attributeValue)) {
|
|
1102
|
-
if (attributeValue.length === 0)
|
|
1103
|
-
attributes += `Bound cluster(s): none `;
|
|
1104
|
-
else {
|
|
1105
|
-
const targets = attributeValue;
|
|
1106
|
-
const formatted = targets.map((target) => {
|
|
1107
|
-
const parts = [];
|
|
1108
|
-
if (target.node !== undefined)
|
|
1109
|
-
parts.push(`node: ${target.node}`);
|
|
1110
|
-
if (target.group !== undefined)
|
|
1111
|
-
parts.push(`group: ${target.group}`);
|
|
1112
|
-
if (target.endpoint !== undefined)
|
|
1113
|
-
parts.push(`endpoint: ${target.endpoint}`);
|
|
1114
|
-
if (target.cluster !== undefined)
|
|
1115
|
-
parts.push(`cluster: ${getClusterNameById(target.cluster)}`);
|
|
1116
|
-
if (target.fabricIndex !== undefined)
|
|
1117
|
-
parts.push(`fabricIndex: ${target.fabricIndex}`);
|
|
1118
|
-
return `[${parts.join(', ')}]`;
|
|
1119
|
-
});
|
|
1120
|
-
attributes += `Bound cluster(s): ${formatted.join(' ')} `;
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
1124
|
-
attributes += `OnOff: ${attributeValue} `;
|
|
1125
|
-
if (clusterName === 'switch' && attributeName === 'currentPosition')
|
|
1126
|
-
attributes += `Position: ${attributeValue} `;
|
|
1127
|
-
if (clusterName === 'windowCovering' && attributeName === 'currentPositionLiftPercent100ths' && isValidNumber(attributeValue, 0, 10000))
|
|
1128
|
-
attributes += `Cover position: ${attributeValue / 100}% `;
|
|
1129
|
-
if (clusterName === 'doorLock' && attributeName === 'lockState')
|
|
1130
|
-
attributes += `State: ${attributeValue === 1 ? 'Locked' : 'Not locked'} `;
|
|
1131
|
-
if (clusterName === 'closureControl' && attributeName === 'overallCurrentState' && attributeValue === null)
|
|
1132
|
-
attributes += `Position: unknown Latch: unknown Speed: unknown SecureState: unknown `;
|
|
1133
|
-
if (clusterName === 'closureControl' && attributeName === 'overallCurrentState' && isValidObject(attributeValue)) {
|
|
1134
|
-
const overallCurrentState = attributeValue;
|
|
1135
|
-
attributes += `Position: ${getEnumDescription(ClosureControl.CurrentPosition, overallCurrentState.position, { fallback: 'unknown' })} `;
|
|
1136
|
-
if (overallCurrentState.latch !== undefined)
|
|
1137
|
-
attributes += `Latch: ${overallCurrentState.latch} `;
|
|
1138
|
-
if (overallCurrentState.speed !== undefined)
|
|
1139
|
-
attributes += `Speed: ${getEnumDescription(ThreeLevelAuto, overallCurrentState.speed)} `;
|
|
1140
|
-
attributes += `SecureState: ${overallCurrentState.secureState ?? 'unknown'} `;
|
|
1141
|
-
}
|
|
1142
|
-
if (clusterName === 'thermostat' && attributeName === 'localTemperature' && isValidNumber(attributeValue))
|
|
1143
|
-
attributes += `Temperature: ${attributeValue / 100}°C `;
|
|
1144
|
-
if (clusterName === 'thermostat' && attributeName === 'occupiedHeatingSetpoint' && isValidNumber(attributeValue))
|
|
1145
|
-
attributes += `Heat to: ${attributeValue / 100}°C `;
|
|
1146
|
-
if (clusterName === 'thermostat' && attributeName === 'occupiedCoolingSetpoint' && isValidNumber(attributeValue))
|
|
1147
|
-
attributes += `Cool to: ${attributeValue / 100}°C `;
|
|
1148
|
-
const modeClusters = new Set(['modeSelect', 'rvcRunMode', 'rvcCleanMode', 'laundryWasherMode', 'ovenMode', 'microwaveOvenMode', 'deviceEnergyManagementMode']);
|
|
1149
|
-
if (modeClusters.has(clusterName) && attributeName === 'supportedModes') {
|
|
1150
|
-
supportedModes = attributeValue;
|
|
1151
|
-
}
|
|
1152
|
-
if (modeClusters.has(clusterName) && attributeName === 'currentMode') {
|
|
1153
|
-
const supportedMode = supportedModes.find((mode) => mode.mode === attributeValue);
|
|
1154
|
-
if (supportedMode)
|
|
1155
|
-
attributes += `Mode: ${supportedMode.label} `;
|
|
1156
|
-
}
|
|
1157
|
-
if (clusterName === 'operationalState' && attributeName === 'operationalState')
|
|
1158
|
-
attributes += `OpState: ${getEnumDescription(OperationalState.OperationalStateEnum, attributeValue)} `;
|
|
1159
|
-
if (clusterName === 'rvcOperationalState' && attributeName === 'operationalState')
|
|
1160
|
-
attributes += `OpState: ${getEnumDescription(RvcOperationalState.OperationalState, attributeValue)} `;
|
|
1161
|
-
if (clusterName === 'pumpConfigurationAndControl' && attributeName === 'operationMode')
|
|
1162
|
-
attributes += `Mode: ${attributeValue} `;
|
|
1163
|
-
if (clusterName === 'valveConfigurationAndControl' && attributeName === 'currentState')
|
|
1164
|
-
attributes += `State: ${getEnumDescription(ValveConfigurationAndControl.ValveState, attributeValue, { fallback: 'unknown' })} `;
|
|
1165
|
-
if (clusterName === 'levelControl' && attributeName === 'currentLevel')
|
|
1166
|
-
attributes += `Level: ${attributeValue} `;
|
|
1167
|
-
if (clusterName === 'applicationBasic' && attributeName === 'applicationName')
|
|
1168
|
-
attributes += `App: ${attributeValue} `;
|
|
1169
|
-
if (clusterName === 'colorControl' && attributeName === 'colorMode' && isValidNumber(attributeValue, 0, 2))
|
|
1170
|
-
attributes += `Mode: ${['HS', 'XY', 'CT'][attributeValue]} `;
|
|
1171
|
-
if (clusterName === 'colorControl' && getAttribute(device, 'colorControl', 'colorMode') === 0 && attributeName === 'currentHue' && isValidNumber(attributeValue))
|
|
1172
|
-
attributes += `Hue: ${Math.round(attributeValue)} `;
|
|
1173
|
-
if (clusterName === 'colorControl' && getAttribute(device, 'colorControl', 'colorMode') === 0 && attributeName === 'currentSaturation' && isValidNumber(attributeValue))
|
|
1174
|
-
attributes += `Saturation: ${Math.round(attributeValue)} `;
|
|
1175
|
-
if (clusterName === 'colorControl' && getAttribute(device, 'colorControl', 'colorMode') === 1 && attributeName === 'currentX' && isValidNumber(attributeValue))
|
|
1176
|
-
attributes += `X: ${Math.round(attributeValue / 655.36) / 100} `;
|
|
1177
|
-
if (clusterName === 'colorControl' && getAttribute(device, 'colorControl', 'colorMode') === 1 && attributeName === 'currentY' && isValidNumber(attributeValue))
|
|
1178
|
-
attributes += `Y: ${Math.round(attributeValue / 655.36) / 100} `;
|
|
1179
|
-
if (clusterName === 'colorControl' && getAttribute(device, 'colorControl', 'colorMode') === 2 && attributeName === 'colorTemperatureMireds' && isValidNumber(attributeValue))
|
|
1180
|
-
attributes += `ColorTemp: ${Math.round(attributeValue)} `;
|
|
1181
|
-
if (clusterName === 'booleanState' && attributeName === 'stateValue')
|
|
1182
|
-
attributes += `Contact: ${attributeValue} `;
|
|
1183
|
-
if (clusterName === 'booleanStateConfiguration' && attributeName === 'alarmsActive' && isValidObject(attributeValue))
|
|
1184
|
-
attributes += `Active alarms: ${stringify(attributeValue)} `;
|
|
1185
|
-
if (clusterName === 'smokeCoAlarm' && attributeName === 'smokeState')
|
|
1186
|
-
attributes += `Smoke: ${getEnumDescription(SmokeCoAlarm.AlarmState, attributeValue)} `;
|
|
1187
|
-
if (clusterName === 'smokeCoAlarm' && attributeName === 'coState')
|
|
1188
|
-
attributes += `Co: ${getEnumDescription(SmokeCoAlarm.AlarmState, attributeValue)} `;
|
|
1189
|
-
if (clusterName === 'fanControl' && attributeName === 'fanMode')
|
|
1190
|
-
attributes += `Mode: ${getEnumDescription(FanControl.FanMode, attributeValue)} `;
|
|
1191
|
-
if (clusterName === 'fanControl' && attributeName === 'percentCurrent')
|
|
1192
|
-
attributes += `Percent: ${attributeValue}% `;
|
|
1193
|
-
if (clusterName === 'fanControl' && attributeName === 'speedCurrent')
|
|
1194
|
-
attributes += `Speed: ${attributeValue} `;
|
|
1195
|
-
if (clusterName === 'fanControl' && attributeName === 'rockSetting' && isValidObject(attributeValue))
|
|
1196
|
-
attributes += `Rock: ${stringify(attributeValue)} `;
|
|
1197
|
-
if (clusterName === 'fanControl' && attributeName === 'windSetting' && isValidObject(attributeValue))
|
|
1198
|
-
attributes += `Wind: ${stringify(attributeValue)} `;
|
|
1199
|
-
if (clusterName === 'fanControl' && attributeName === 'airflowDirection')
|
|
1200
|
-
attributes += `Direction: ${getEnumDescription(FanControl.AirflowDirection, attributeValue)} `;
|
|
1201
|
-
if (clusterName === 'hepaFilterMonitoring' && attributeName === 'condition')
|
|
1202
|
-
attributes += `Hepa filter: ${attributeValue}% `;
|
|
1203
|
-
if (clusterName === 'activatedCarbonFilterMonitoring' && attributeName === 'condition')
|
|
1204
|
-
attributes += `Carbon filter: ${attributeValue}% `;
|
|
1205
|
-
if (clusterName === 'waterTankLevelMonitoring' && attributeName === 'condition')
|
|
1206
|
-
attributes += `Water tank: ${attributeValue}% `;
|
|
1207
|
-
if (clusterName === 'temperatureAlarm' && attributeName === 'state' && isValidObject(attributeValue))
|
|
1208
|
-
attributes += `Temp alarm: ${stringify(attributeValue)} `;
|
|
1209
|
-
if (clusterName === 'occupancySensing' && attributeName === 'occupancy' && isValidObject(attributeValue, 1))
|
|
1210
|
-
attributes += `Occupancy: ${attributeValue.occupied} `;
|
|
1211
|
-
if (clusterName === 'illuminanceMeasurement' && attributeName === 'measuredValue') {
|
|
1212
|
-
if (attributeValue === null)
|
|
1213
|
-
attributes += `Illuminance: unknown `;
|
|
1214
|
-
else if (isValidNumber(attributeValue))
|
|
1215
|
-
attributes += `Illuminance: ${Math.round(Math.max(Math.pow(10, attributeValue / 10000), 0))} lx`;
|
|
1216
|
-
}
|
|
1217
|
-
if (clusterName === 'airQuality' && attributeName === 'airQuality')
|
|
1218
|
-
attributes += `Air quality: ${getEnumDescription(AirQuality.AirQualityEnum, attributeValue)} `;
|
|
1219
|
-
if (clusterName === 'totalVolatileOrganicCompoundsConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
1220
|
-
appendMeasurement('Voc', getMeasurementText(attributeValue, 1, ''));
|
|
1221
|
-
if (clusterName === 'pm1ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
1222
|
-
appendMeasurement('Pm1', getMeasurementText(attributeValue, 1, ''));
|
|
1223
|
-
if (clusterName === 'pm25ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
1224
|
-
appendMeasurement('Pm2.5', getMeasurementText(attributeValue, 1, ''));
|
|
1225
|
-
if (clusterName === 'pm10ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
1226
|
-
appendMeasurement('Pm10', getMeasurementText(attributeValue, 1, ''));
|
|
1227
|
-
if (clusterName === 'formaldehydeConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
1228
|
-
appendMeasurement('CH₂O', getMeasurementText(attributeValue, 1, ''));
|
|
1229
|
-
if (clusterName === 'temperatureMeasurement' && attributeName === 'measuredValue')
|
|
1230
|
-
appendMeasurement('Temperature', getMeasurementText(attributeValue, 100, ' °C'));
|
|
1231
|
-
if (clusterName === 'relativeHumidityMeasurement' && attributeName === 'measuredValue')
|
|
1232
|
-
appendMeasurement('Humidity', getMeasurementText(attributeValue, 100, '%'));
|
|
1233
|
-
if (clusterName === 'pressureMeasurement' && attributeName === 'measuredValue')
|
|
1234
|
-
appendMeasurement('Pressure', getMeasurementText(attributeValue, 1, ' hPa'));
|
|
1235
|
-
if (clusterName === 'flowMeasurement' && attributeName === 'measuredValue')
|
|
1236
|
-
appendMeasurement('Flow', getMeasurementText(attributeValue, 10, ' m³/h'));
|
|
1237
|
-
if (clusterId === SoilMeasurement.id && attributeName === 'soilMoistureMeasuredValue')
|
|
1238
|
-
appendMeasurement('Soil moisture', getMeasurementText(attributeValue, 1, '%'));
|
|
1239
|
-
if (clusterName === 'electricalPowerMeasurement' && attributeName === 'voltage')
|
|
1240
|
-
appendMeasurement('Voltage', getMeasurementText(attributeValue, 1_000, 'V'));
|
|
1241
|
-
if (clusterName === 'electricalPowerMeasurement' && attributeName === 'activeCurrent')
|
|
1242
|
-
appendMeasurement('Current', getMeasurementText(attributeValue, 1_000, 'A'));
|
|
1243
|
-
if (clusterName === 'electricalPowerMeasurement' && attributeName === 'activePower')
|
|
1244
|
-
appendMeasurement('Power', getMeasurementText(attributeValue, 1_000_000, 'kW'));
|
|
1245
|
-
if (clusterName === 'electricalPowerMeasurement' && attributeName === 'frequency')
|
|
1246
|
-
appendMeasurement('Frequency', getMeasurementText(attributeValue, 1_000, 'Hz'));
|
|
1247
|
-
if (clusterName === 'electricalEnergyMeasurement' && attributeName === 'cumulativeEnergyImported')
|
|
1248
|
-
appendMeasurement('Imported', getEnergyText(attributeValue));
|
|
1249
|
-
if (clusterName === 'electricalEnergyMeasurement' && attributeName === 'cumulativeEnergyExported')
|
|
1250
|
-
appendMeasurement('Exported', getEnergyText(attributeValue));
|
|
1251
|
-
if (clusterName === 'deviceEnergyManagement' && attributeName === 'esaCanGenerate')
|
|
1252
|
-
attributes += `ESA can generate: ${attributeValue} `;
|
|
1253
|
-
if (clusterName === 'deviceEnergyManagement' && attributeName === 'esaState')
|
|
1254
|
-
attributes += `ESA state: ${DeviceEnergyManagement.EsaState[attributeValue]} `;
|
|
1255
|
-
if (clusterName === 'fixedLabel' && attributeName === 'labelList')
|
|
1256
|
-
attributes += `${getFixedLabel(device)} `;
|
|
1257
|
-
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1258
|
-
attributes += `${getUserLabel(device)} `;
|
|
1259
|
-
});
|
|
1260
|
-
return attributes.trimStart().trimEnd();
|
|
1261
|
-
}
|
|
1262
981
|
getApiPlugins() {
|
|
1263
982
|
if (this.matterbridge.hasCleanupStarted)
|
|
1264
983
|
return [];
|
|
@@ -1293,7 +1012,7 @@ export class Frontend extends EventEmitter {
|
|
|
1293
1012
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1294
1013
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1295
1014
|
frontendPath: plugin.frontendPath,
|
|
1296
|
-
matter: plugin.serverNode ?
|
|
1015
|
+
matter: plugin.serverNode ? getServerNodeData(plugin.serverNode) : undefined,
|
|
1297
1016
|
});
|
|
1298
1017
|
}
|
|
1299
1018
|
return plugins;
|
|
@@ -1316,23 +1035,23 @@ export class Frontend extends EventEmitter {
|
|
|
1316
1035
|
productUrl: device.productUrl,
|
|
1317
1036
|
configUrl: device.configUrl,
|
|
1318
1037
|
uniqueId: device.uniqueId,
|
|
1319
|
-
reachable:
|
|
1320
|
-
powerSource:
|
|
1321
|
-
batteryLevel:
|
|
1322
|
-
matter: device.mode === 'server' && device.serverNode ?
|
|
1323
|
-
cluster:
|
|
1038
|
+
reachable: getReachability(device),
|
|
1039
|
+
powerSource: getPowerSource(device),
|
|
1040
|
+
batteryLevel: getBatteryLevel(device),
|
|
1041
|
+
matter: device.mode === 'server' && device.serverNode ? getServerNodeData(device.serverNode) : undefined,
|
|
1042
|
+
cluster: getClusterTextFromDevice(device),
|
|
1324
1043
|
});
|
|
1325
1044
|
}
|
|
1326
1045
|
return devices;
|
|
1327
1046
|
}
|
|
1328
|
-
|
|
1047
|
+
getApiCluster(pluginName, endpointNumber, serialNumber, uniqueId) {
|
|
1329
1048
|
if (this.matterbridge.hasCleanupStarted)
|
|
1330
1049
|
return;
|
|
1331
1050
|
const endpoint = this.matterbridge.devices
|
|
1332
1051
|
.array()
|
|
1333
1052
|
.find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber && (!serialNumber || d.serialNumber === serialNumber) && (!uniqueId || d.uniqueId === uniqueId));
|
|
1334
1053
|
if (!endpoint?.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1335
|
-
this.log.error(`
|
|
1054
|
+
this.log.error(`getApiCluster: no device found for plugin ${pluginName} and endpoint number ${endpointNumber} (serial: ${serialNumber ?? 'N/A'}, uniqueId: ${uniqueId ?? 'N/A'})`);
|
|
1336
1055
|
return;
|
|
1337
1056
|
}
|
|
1338
1057
|
const deviceTypes = [];
|
|
@@ -1361,7 +1080,7 @@ export class Frontend extends EventEmitter {
|
|
|
1361
1080
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1362
1081
|
childEndpoints.forEach((childEndpoint) => {
|
|
1363
1082
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1364
|
-
this.log.error(`
|
|
1083
|
+
this.log.error(`getApiCluster: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1365
1084
|
return;
|
|
1366
1085
|
}
|
|
1367
1086
|
const deviceTypes = [];
|
|
@@ -1748,7 +1467,7 @@ export class Frontend extends EventEmitter {
|
|
|
1748
1467
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Unknown server node id ${localData.params.id} in /api/matter` });
|
|
1749
1468
|
return;
|
|
1750
1469
|
}
|
|
1751
|
-
const matter =
|
|
1470
|
+
const matter = getServerNodeData(serverNode);
|
|
1752
1471
|
this.log.debug(`*Server node ${serverNode.id}: commissioned ${serverNode.state.commissioning.commissioned} upTime ${serverNode.state.generalDiagnostics.upTime}.`);
|
|
1753
1472
|
if (data.params.server) {
|
|
1754
1473
|
this.log.debug(`*Sending data for node ${data.params.id}`);
|
|
@@ -1756,13 +1475,13 @@ export class Frontend extends EventEmitter {
|
|
|
1756
1475
|
}
|
|
1757
1476
|
if (data.params.startCommission) {
|
|
1758
1477
|
await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1759
|
-
|
|
1478
|
+
setAdvertisingNode(serverNode.id, Date.now());
|
|
1760
1479
|
this.log.debug(`*Allow commissioning has been sent for node ${data.params.id}`);
|
|
1761
1480
|
this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: Date.now(), advertising: true } });
|
|
1762
1481
|
}
|
|
1763
1482
|
if (data.params.stopCommission) {
|
|
1764
1483
|
await serverNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
1765
|
-
|
|
1484
|
+
deleteAdvertisingNode(serverNode.id);
|
|
1766
1485
|
this.log.debug(`*End commissioning has been sent for node ${data.params.id}`);
|
|
1767
1486
|
this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
|
|
1768
1487
|
}
|
|
@@ -1812,9 +1531,9 @@ export class Frontend extends EventEmitter {
|
|
|
1812
1531
|
return;
|
|
1813
1532
|
}
|
|
1814
1533
|
const start = performance.now();
|
|
1815
|
-
const clusters = this.
|
|
1534
|
+
const clusters = this.getApiCluster(data.params.plugin, data.params.endpoint, data.params.serialNumber, data.params.uniqueId);
|
|
1816
1535
|
if (this.diagnostic)
|
|
1817
|
-
writeDiagnostic(`Frontend:${data.sender}`, `
|
|
1536
|
+
writeDiagnostic(`Frontend:${data.sender}`, `getApiCluster() took ${(performance.now() - start).toFixed(2)} ms`);
|
|
1818
1537
|
if (clusters) {
|
|
1819
1538
|
sendResponse({
|
|
1820
1539
|
id: data.id,
|
package/dist/matterNode.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import '@matter/nodejs';
|
|
2
2
|
import EventEmitter from 'node:events';
|
|
3
3
|
import { type Diagnostic, type StorageContext, type StorageManager, StorageService } from '@matter/general';
|
|
4
|
-
import { Endpoint, ServerNode
|
|
4
|
+
import { Endpoint, ServerNode } from '@matter/node';
|
|
5
5
|
import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
|
|
6
|
-
import { type DeviceCertification,
|
|
6
|
+
import { type DeviceCertification, MdnsService } from '@matter/protocol';
|
|
7
7
|
import { DeviceTypeId, VendorId } from '@matter/types';
|
|
8
|
-
import type {
|
|
8
|
+
import type { PluginName, SharedMatterbridge, WorkerMessage } from '@matterbridge/types';
|
|
9
9
|
import type { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
10
10
|
import { type Plugin, PluginManager } from './pluginManager.js';
|
|
11
11
|
interface MatterEvents {
|
|
@@ -42,7 +42,6 @@ export declare class MatterNode extends EventEmitter<MatterEvents> {
|
|
|
42
42
|
aggregatorDeviceType: DeviceTypeId;
|
|
43
43
|
aggregatorSerialNumber: string | undefined;
|
|
44
44
|
aggregatorUniqueId: string | undefined;
|
|
45
|
-
advertisingNodes: Map<string, number>;
|
|
46
45
|
readonly pluginManager: PluginManager;
|
|
47
46
|
readonly dependantMatterNodes: Map<string, MatterNode>;
|
|
48
47
|
private readonly server;
|
|
@@ -60,7 +59,6 @@ export declare class MatterNode extends EventEmitter<MatterEvents> {
|
|
|
60
59
|
stopMatterStorage(): Promise<void>;
|
|
61
60
|
createServerNodeContext(storeId: string, deviceName: string, deviceType: DeviceTypeId, vendorId: VendorId, vendorName: string, productId: number, productName: string, serialNumber?: string, uniqueId?: string, softwareVersion?: number, softwareVersionString?: string, hardwareVersion?: number, hardwareVersionString?: string, productLabel?: string, productUrl?: string, configurationVersion?: number): Promise<StorageContext>;
|
|
62
61
|
createServerNode(port?: number, passcode?: number, discriminator?: number): Promise<ServerNode>;
|
|
63
|
-
getServerNodeData(serverNode: ServerNode): ApiMatter;
|
|
64
62
|
startServerNode(timeout?: number): Promise<void>;
|
|
65
63
|
stopServerNode(timeout?: number): Promise<void>;
|
|
66
64
|
createAggregatorNode(): Promise<Endpoint<AggregatorEndpoint>>;
|
|
@@ -73,11 +71,8 @@ export declare class MatterNode extends EventEmitter<MatterEvents> {
|
|
|
73
71
|
removeAllBridgedEndpoints(pluginName: string, delay?: number): Promise<void>;
|
|
74
72
|
addVirtualEndpoint(pluginName: string, name: string, type: 'light' | 'outlet' | 'switch' | 'mounted_switch', callback: () => Promise<void>): Promise<boolean>;
|
|
75
73
|
subscribeAttributeChanged(plugin: Plugin, device: MatterbridgeEndpoint): Promise<void>;
|
|
76
|
-
sanitizeFabricInformations(fabricInfo: ExposedFabricInformation[]): SanitizedExposedFabricInformation[];
|
|
77
|
-
sanitizeSessionInformation(sessions: SessionsBehavior.Session[]): SanitizedSession[];
|
|
78
74
|
setServerReachability(reachable: boolean): Promise<void>;
|
|
79
75
|
setAggregatorReachability(aggregatorNode: Endpoint<AggregatorEndpoint>, reachable: boolean): Promise<void>;
|
|
80
|
-
getVendorIdName: (vendorId: number | undefined) => string;
|
|
81
76
|
yieldToNode(timeout?: number): Promise<void>;
|
|
82
77
|
}
|
|
83
78
|
export {};
|