Skip to content

Commit

Permalink
Dev 2.1.5-dev.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Luligu committed Feb 9, 2025
1 parent 25dc42d commit f4bffe2
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 36 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,18 @@ matterbridge-zigbee2mqtt v. 2.4.4
matterbridge-somfy-tahoma v. 1.2.3
matterbridge-hass v. 0.0.8

## [2.1.5] - 2025-02-08
## [2.1.5] - 2025-02-09

### Added

- [frontend]: Frontend v.2.4.1.
- [frontend]: Optimized rendering of all pages.
- [frontend]: Added cpuUsed, rss and heapUsed to SystemInformation.

### Changed

- [matterbridge]: Calls getNpmPackageVersion() instead of npm to get latest version.
- [matterbridge]: Memory optimization on MatterbridgeEndpoint.

### Fixed

Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "matterbridge",
"version": "2.1.5-dev.2",
"version": "2.1.5-dev.3",
"description": "Matterbridge plugin manager for Matter",
"author": "https://github.com/Luligu",
"license": "Apache-2.0",
Expand Down Expand Up @@ -174,4 +174,4 @@
"typescript": "5.7.3",
"typescript-eslint": "8.23.0"
}
}
}
159 changes: 131 additions & 28 deletions src/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @file frontend.ts
* @author Luca Liguori
* @date 2025-01-13
* @version 1.0.1
* @version 1.0.2
*
* Copyright 2025, 2026, 2027 Luca Liguori.
*
Expand Down Expand Up @@ -37,7 +37,7 @@ import { promises as fs } from 'fs';
import { AnsiLogger, CYAN, db, debugStringify, er, LogLevel, nf, rs, stringify, TimestampFormat, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';

// Matterbridge
import { createZip, getIntParameter, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
import { createZip, deepCopy, getIntParameter, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
import { ApiClusters, ApiDevices, BaseRegisteredPlugin, plg, RegisteredPlugin } from './matterbridgeTypes.js';
import { Matterbridge } from './matterbridge.js';
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
Expand All @@ -60,6 +60,18 @@ export const WS_ID_REFRESH_NEEDED = 1;
*/
export const WS_ID_RESTART_NEEDED = 2;

/**
* Websocket message ID indicating a cpu update.
* @constant {number}
*/
export const WS_ID_CPU_UPDATE = 3;

/**
* Websocket message ID indicating a memory update.
* @constant {number}
*/
export const WS_ID_MEMORY_UPDATE = 4;

/**
* Initializes the frontend of Matterbridge.
*
Expand All @@ -76,7 +88,8 @@ export class Frontend {
private httpsServer: https.Server | undefined;
private webSocketServer: WebSocketServer | undefined;

private prevCpus: os.CpuInfo[] = os.cpus();
private prevCpus: os.CpuInfo[] = deepCopy(os.cpus());
private lastCpuUsage = 0;
private memoryData: (NodeJS.MemoryUsage & { cpu: string })[] = [];
private memoryInterval?: NodeJS.Timeout;
private memoryTimeout?: NodeJS.Timeout;
Expand Down Expand Up @@ -985,37 +998,73 @@ export class Frontend {
}
}

// Function to format bytes to KB or MB
// Function to format bytes to KB, MB, or GB
private formatMemoryUsage = (bytes: number): string => {
const kb = bytes / 1024;
const mb = kb / 1024;
return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`;
if (bytes >= 1024 ** 3) {
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
} else if (bytes >= 1024 ** 2) {
return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
} else {
return `${(bytes / 1024).toFixed(2)} KB`;
}
};

// Function to format system uptime with only the most significant unit
private formatOsUpTime = (): string => {
const seconds = os.uptime();

if (seconds >= 86400) {
const days = Math.floor(seconds / 86400);
return `${days} day${days !== 1 ? 's' : ''}`;
}
if (seconds >= 3600) {
const hours = Math.floor(seconds / 3600);
return `${hours} hour${hours !== 1 ? 's' : ''}`;
}
if (seconds >= 60) {
const minutes = Math.floor(seconds / 60);
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
}
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
};

private getCpuUsage = () => {
const currCpus = os.cpus();
if (currCpus.length !== this.prevCpus.length) {
this.prevCpus = deepCopy(currCpus); // Reset the previous cpus
this.log.debug(`***Cpu usage reset. Current cpus: ${currCpus.length}. Previous cpus: ${this.prevCpus.length}.`);
return this.lastCpuUsage.toFixed(2);
}
let totalIdle = 0,
totalTick = 0;

// Get the cpu usage
this.prevCpus.forEach((prevCpu, i) => {
const currCpu = currCpus[i];

const idleDiff = currCpu.times.idle - prevCpu.times.idle;
const totalDiff = (Object.keys(currCpu.times) as (keyof typeof currCpu.times)[]).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);

totalIdle += idleDiff;
totalTick += totalDiff;
});
const cpuUsage = 100 - (totalIdle / totalTick) * 100;
if (totalTick === 0 || isNaN(cpuUsage) || !isFinite(cpuUsage) || cpuUsage <= 0) {
this.log.debug('***Invalid cpu usage. Returning the previous one.');
return this.lastCpuUsage.toFixed(2);
}
this.prevCpus = deepCopy(currCpus);
this.lastCpuUsage = cpuUsage;
return cpuUsage.toFixed(2);
};

private startCpuMemoryDump() {
clearInterval(this.memoryInterval);
clearTimeout(this.memoryTimeout);

const interval = () => {
const currCpus = os.cpus();
if (currCpus.length !== this.prevCpus.length) {
this.prevCpus = currCpus; // Reset the previous cpus if the number of cpu has changed
}
let totalIdle = 0,
totalTick = 0;

// Get the cpu usage
this.prevCpus.forEach((prevCpu, i) => {
const currCpu = currCpus[i];

const idleDiff = currCpu.times.idle - prevCpu.times.idle;
const totalDiff = (Object.keys(currCpu.times) as (keyof typeof currCpu.times)[]).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);

totalIdle += idleDiff;
totalTick += totalDiff;
});
const cpuUsage = (100 - (totalIdle / totalTick) * 100).toFixed(2);
this.prevCpus = currCpus;
const cpuUsage = this.getCpuUsage();

// Get the memory usage
const memoryUsageRaw = process.memoryUsage();
Expand All @@ -1031,15 +1080,34 @@ export class Frontend {
this.log.debug(
`***Cpu usage: ${CYAN}${cpuUsage.padStart(6, ' ')} %${db} - Memory usage: rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}`,
);

// Update the system information
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime();
this.matterbridge.systemInformation.cpuUsed = cpuUsage;
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);

this.wssSendCpuUpdate(this.matterbridge.systemInformation.cpuUsed);
this.wssSendMemoryUpdate(
this.matterbridge.systemInformation.freeMemory,
this.matterbridge.systemInformation.totalMemory,
this.matterbridge.systemInformation.systemUptime,
this.matterbridge.systemInformation.rss,
this.matterbridge.systemInformation.heapUsed,
this.matterbridge.systemInformation.heapTotal,
);
};
interval();
this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000);
this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000); // 1 second
this.memoryInterval.unref();
this.memoryTimeout = setTimeout(
() => {
this.stopCpuMemoryDump();
},
getIntParameter('memorytimeout') ?? 600000,
getIntParameter('memorytimeout') ?? 600000, // 10 minutes
);
this.memoryTimeout.unref();
}
Expand Down Expand Up @@ -1072,8 +1140,13 @@ export class Frontend {
*/
private async getApiSettings() {
// Update the system information
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime();
this.matterbridge.systemInformation.cpuUsed = this.getCpuUsage();
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
this.matterbridge.systemInformation.heap = this.formatMemoryUsage(process.memoryUsage().heapUsed) + ' / ' + this.formatMemoryUsage(process.memoryUsage().heapTotal);
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);

// Update the matterbridge information
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
Expand Down Expand Up @@ -1541,4 +1614,34 @@ export class Frontend {
}
});
}

/**
* Sends a memory update message to all connected clients.
*
*/
wssSendCpuUpdate(cpuUsed: string) {
this.log.debug('Sending a memory update message to all connected clients');
this.matterbridge.matterbridgeInformation.restartRequired = true;
// Send the message to all connected clients
this.webSocketServer?.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsed } }));
}
});
}

/**
* Sends a cpu update message to all connected clients.
*
*/
wssSendMemoryUpdate(freeMemory: string, totalMemory: string, systemUptime: string, rss: string, heapUsed: string, heapTotal: string) {
this.log.debug('Sending a cpu update message to all connected clients');
this.matterbridge.matterbridgeInformation.restartRequired = true;
// Send the message to all connected clients
this.webSocketServer?.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { freeMemory, totalMemory, systemUptime, rss, heapUsed, heapTotal } }));
}
});
}
}

0 comments on commit f4bffe2

Please sign in to comment.