@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 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
  }
@@ -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);
@@ -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
  }
@@ -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
- getClusters(pluginName: string, endpointNumber: number, serialNumber?: string, uniqueId?: string): ApiClusters | undefined;
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, Lifecycle, LogDestination, LogFormat as MatterLogFormat, Logger, LogLevel as MatterLogLevel } from '@matter/general';
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, getAttribute } from './matterbridgeEndpointHelpers.js';
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(process.memoryUsage().rss);
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 ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
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: this.getReachability(device),
1320
- powerSource: this.getPowerSource(device),
1321
- batteryLevel: this.getBatteryLevel(device),
1322
- matter: device.mode === 'server' && device.serverNode ? this.matterbridge.getServerNodeData(device.serverNode) : undefined,
1323
- cluster: this.getClusterTextFromDevice(device),
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
- getClusters(pluginName, endpointNumber, serialNumber, uniqueId) {
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(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber} (serial: ${serialNumber ?? 'N/A'}, uniqueId: ${uniqueId ?? 'N/A'})`);
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(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
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 = this.matterbridge.getServerNodeData(serverNode);
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
- this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
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
- this.matterbridge.advertisingNodes.delete(serverNode.id);
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.getClusters(data.params.plugin, data.params.endpoint, data.params.serialNumber, data.params.uniqueId);
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}`, `getClusters() took ${(performance.now() - start).toFixed(2)} ms`);
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,
@@ -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, type SessionsBehavior } from '@matter/node';
4
+ import { Endpoint, ServerNode } from '@matter/node';
5
5
  import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
6
- import { type DeviceCertification, type ExposedFabricInformation, MdnsService } from '@matter/protocol';
6
+ import { type DeviceCertification, MdnsService } from '@matter/protocol';
7
7
  import { DeviceTypeId, VendorId } from '@matter/types';
8
- import type { ApiMatter, PluginName, SanitizedExposedFabricInformation, SanitizedSession, SharedMatterbridge, WorkerMessage } from '@matterbridge/types';
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 {};