Add system/hardware info to the Synology page
Beyond volume/disk health, DSM exposes hostname, CPU, RAM, IP addresses, model/serial/firmware, and uptime -- surface those too as a "System" card on the Synology page. New adapter.getSystemInfo() combines three DSM Web API calls (fields confirmed against the actively-maintained mib1185/py-synologydsm-api client, which documents the same SYNO.Core.System / SYNO.Core.System. Utilization / SYNO.DSM.Network endpoints this adapter already uses the discover-then-call pattern for): - SYNO.Core.System "info" -- model, serial, firmware_ver, cpu_cores, cpu_clock_speed, up_time (a raw uptime-command-style string, not a duration -- parsed client-side into "5d 15h 2m" with a fallback to the raw string if DSM ever returns an unexpected format). - SYNO.Core.System.Utilization "get" -- live CPU load % (user+system+ other) and real memory usage, in KB. - SYNO.DSM.Network "list" -- hostname and interface IP addresses (loopback filtered out). New GET /:id/synology/system route alongside the existing /storage one, read-only like the rest of this integration. Verified end-to-end against a local mock DSM server standing in for the real API (login/session flow, all three endpoint shapes, IP filtering, byte math) since this sandbox can't reach the user's LAN; also unit-checked the client-side uptime regex against DSM's three uptime-command formats (with/without days, minutes-only) plus its unmatched-format fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
95bd831d81
commit
9f2d27e2c4
4 files changed
+197
-2
No files matched your search
@@ -55,9 +55,28 @@ export interface SynologyStorageInfo {
|
||||
disks: SynologyDisk[];
|
||||
}
|
||||
|
||||
export interface SynologySystemInfo {
|
||||
hostname: string | null;
|
||||
model: string | null;
|
||||
serial: string | null;
|
||||
firmwareVersion: string | null;
|
||||
uptime: string | null;
|
||||
ipAddresses: string[];
|
||||
cpu: {
|
||||
cores: number | null;
|
||||
clockSpeedMHz: number | null;
|
||||
loadPercent: number | null;
|
||||
};
|
||||
memory: {
|
||||
totalBytes: number | null;
|
||||
usedBytes: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SynologyAdapter {
|
||||
ping(): Promise<{ ok: boolean; latencyMs?: number; error?: string }>;
|
||||
getStorageInfo(): Promise<SynologyStorageInfo>;
|
||||
getSystemInfo(): Promise<SynologySystemInfo>;
|
||||
}
|
||||
|
||||
interface RawResponse {
|
||||
@@ -218,5 +237,48 @@ export function createSynologyAdapter(config: SynologyConfig): SynologyAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
return { ping, getStorageInfo };
|
||||
async function getSystemInfo(): Promise<SynologySystemInfo> {
|
||||
const [info, utilization, network] = await Promise.all([
|
||||
callApi("SYNO.Core.System", "info"),
|
||||
callApi("SYNO.Core.System.Utilization", "get"),
|
||||
callApi("SYNO.DSM.Network", "list"),
|
||||
]);
|
||||
|
||||
const cpuLoadPercent =
|
||||
utilization.cpu?.user_load !== undefined
|
||||
? Number(utilization.cpu.user_load) + Number(utilization.cpu.system_load) + Number(utilization.cpu.other_load)
|
||||
: null;
|
||||
|
||||
const memTotalBytes = utilization.memory?.total_real !== undefined ? Number(utilization.memory.total_real) * 1024 : null;
|
||||
const memAvailBytes = utilization.memory?.avail_real !== undefined ? Number(utilization.memory.avail_real) * 1024 : null;
|
||||
|
||||
const ipAddresses: string[] = [];
|
||||
for (const iface of network.interfaces ?? []) {
|
||||
for (const ip of iface.ip ?? []) {
|
||||
if (ip.address && ip.address !== "127.0.0.1" && !ipAddresses.includes(ip.address)) {
|
||||
ipAddresses.push(ip.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hostname: network.hostname ?? null,
|
||||
model: info.model ?? null,
|
||||
serial: info.serial ?? null,
|
||||
firmwareVersion: info.firmware_ver ?? null,
|
||||
uptime: info.up_time ?? null,
|
||||
ipAddresses,
|
||||
cpu: {
|
||||
cores: info.cpu_cores !== undefined ? Number(info.cpu_cores) : null,
|
||||
clockSpeedMHz: info.cpu_clock_speed !== undefined ? Number(info.cpu_clock_speed) : null,
|
||||
loadPercent: cpuLoadPercent,
|
||||
},
|
||||
memory: {
|
||||
totalBytes: memTotalBytes,
|
||||
usedBytes: memTotalBytes !== null && memAvailBytes !== null ? memTotalBytes - memAvailBytes : null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return { ping, getStorageInfo, getSystemInfo };
|
||||
}
|
||||
@@ -704,3 +704,15 @@ integrationsRouter.get("/:id/synology/storage", asyncHandler(async (req, res) =>
|
||||
res.status(502).json({ error: err instanceof Error ? err.message : String(err) });
|
||||
}
|
||||
}));
|
||||
|
||||
integrationsRouter.get("/:id/synology/system", asyncHandler(async (req, res) => {
|
||||
const found = await requireSynologyAdapter(req, res);
|
||||
if (!found) return;
|
||||
|
||||
try {
|
||||
const info = await found.adapter.getSystemInfo();
|
||||
res.json(info);
|
||||
} catch (err) {
|
||||
res.status(502).json({ error: err instanceof Error ? err.message : String(err) });
|
||||
}
|
||||
}));
|
||||
Reference in new issue
Block a user