Add update script, enhance admin features, and improve styles
- Created a PowerShell script (update.ps1) for automating the process of pulling the latest repository changes, stopping, building, and starting the Docker Compose stack. - Updated App.jsx to include new admin sections for alarms and integrations, added state management for runner jobs, and enhanced permission checks. - Extended the API module (api.js) with new endpoints for managing integrations and runner jobs. - Improved CSS styles for a more compact and responsive layout in the admin interface, focusing on source and datapoint cards.
This commit is contained in:
@@ -120,16 +120,19 @@ function getBroadcastTargets(routing, body = {}) {
|
||||
if (!isAutoBroadcast(configured)) {
|
||||
targets.push(configured);
|
||||
}
|
||||
for (const subnet of parseHostList(body.scanSubnets || body.bacnetScanSubnets || body.options?.bacnetScanSubnets)) {
|
||||
const configuredSubnets = parseHostList(body.scanSubnets || body.bacnetScanSubnets || body.options?.bacnetScanSubnets);
|
||||
for (const subnet of configuredSubnets) {
|
||||
const broadcast = cidrBroadcast(subnet);
|
||||
if (broadcast) targets.push(broadcast);
|
||||
}
|
||||
for (const candidate of [body.clientHost, body.browserHost, body.locationHost]) {
|
||||
const broadcast = broadcastFromAddress(candidate);
|
||||
if (broadcast) targets.push(broadcast);
|
||||
if (!configuredSubnets.length) {
|
||||
for (const candidate of [body.clientHost, body.browserHost, body.locationHost]) {
|
||||
const broadcast = broadcastFromAddress(candidate);
|
||||
if (broadcast) targets.push(broadcast);
|
||||
}
|
||||
targets.push(...getInterfaceBroadcasts());
|
||||
targets.push(...parseHostList(process.env.BACNET_BROADCAST_FALLBACKS));
|
||||
}
|
||||
targets.push(...getInterfaceBroadcasts());
|
||||
targets.push(...parseHostList(process.env.BACNET_BROADCAST_FALLBACKS));
|
||||
if (!targets.length) {
|
||||
targets.push("255.255.255.255");
|
||||
}
|
||||
@@ -413,6 +416,20 @@ function extractValue(propertyValue) {
|
||||
return propertyValue?.values?.[0]?.value ?? propertyValue?.values?.[0] ?? null;
|
||||
}
|
||||
|
||||
function bacnetUnitSymbol(value) {
|
||||
const unit = Number(value);
|
||||
const map = {
|
||||
3: "A",
|
||||
5: "V",
|
||||
53: "Pa",
|
||||
62: "°C",
|
||||
63: "K",
|
||||
95: "",
|
||||
98: "%",
|
||||
};
|
||||
return Object.prototype.hasOwnProperty.call(map, unit) ? map[unit] : "";
|
||||
}
|
||||
|
||||
async function scanObjects(client, device, portNumber) {
|
||||
const host = device.address;
|
||||
const deviceObject = { type: 8, instance: Number(device.deviceId) };
|
||||
@@ -429,6 +446,7 @@ async function scanObjects(client, device, portNumber) {
|
||||
const objectName = await readProperty(client, host, objectId, 77);
|
||||
const description = await readProperty(client, host, objectId, 28);
|
||||
const presentValue = await readProperty(client, host, objectId, 85);
|
||||
const units = await readProperty(client, host, objectId, 117);
|
||||
const raw = extractValue(presentValue);
|
||||
const name = normalizeText(extractValue(objectName)) || objectLabel(objectId);
|
||||
const alias = normalizeText(extractValue(description)) || name;
|
||||
@@ -438,6 +456,7 @@ async function scanObjects(client, device, portNumber) {
|
||||
host,
|
||||
port: portNumber,
|
||||
deviceId: device.deviceId,
|
||||
deviceName: device.name || "BACnet Gerät " + device.deviceId,
|
||||
objectType: objectId.type,
|
||||
objectInstance: objectId.instance,
|
||||
propertyId: 85,
|
||||
@@ -447,6 +466,7 @@ async function scanObjects(client, device, portNumber) {
|
||||
description: alias,
|
||||
alias,
|
||||
kind: typeof raw === "boolean" || (objectId.type >= 3 && objectId.type <= 5) ? "digital" : "analog",
|
||||
unit: typeof raw === "boolean" || (objectId.type >= 3 && objectId.type <= 5) ? "bool" : bacnetUnitSymbol(extractValue(units)),
|
||||
currentValue: raw,
|
||||
hasValue: raw !== null,
|
||||
});
|
||||
@@ -646,15 +666,25 @@ function isLikelyDockerGatewayAddress(address) {
|
||||
|
||||
function preferRealBacnetAddresses(devices) {
|
||||
const list = Array.isArray(devices) ? devices : [];
|
||||
const idsWithRealAddress = new Set(
|
||||
list
|
||||
.filter((device) => !isLikelyDockerGatewayAddress(device.address || device.host))
|
||||
.map((device) => String(device.deviceId)),
|
||||
);
|
||||
return list.filter((device) => {
|
||||
const address = device.address || device.host;
|
||||
return !(idsWithRealAddress.has(String(device.deviceId)) && isLikelyDockerGatewayAddress(address));
|
||||
});
|
||||
const byDeviceId = new Map();
|
||||
for (const device of list) {
|
||||
const id = String(device?.deviceId || "");
|
||||
if (!id) continue;
|
||||
const address = device.address || device.host || "";
|
||||
const current = byDeviceId.get(id);
|
||||
if (!current) {
|
||||
byDeviceId.set(id, device);
|
||||
continue;
|
||||
}
|
||||
const currentAddress = current.address || current.host || "";
|
||||
const deviceIsReal = !isLikelyDockerGatewayAddress(address);
|
||||
const currentIsReal = !isLikelyDockerGatewayAddress(currentAddress);
|
||||
const hasMorePoints = Number(device.pointCount || device.points?.length || 0) > Number(current.pointCount || current.points?.length || 0);
|
||||
if ((deviceIsReal && !currentIsReal) || (deviceIsReal === currentIsReal && hasMorePoints)) {
|
||||
byDeviceId.set(id, { ...current, ...device, points: device.points?.length ? device.points : current.points });
|
||||
}
|
||||
}
|
||||
return Array.from(byDeviceId.values());
|
||||
}
|
||||
async function scanNetwork(body) {
|
||||
const timeout = Number(body.timeout || defaultTimeout);
|
||||
@@ -706,7 +736,7 @@ async function scanNetwork(body) {
|
||||
await waitForSocket(client);
|
||||
await registerForeignDevice(client, routing);
|
||||
sendWhoIs(client, routing, body.deviceId);
|
||||
await sleep(Math.min(timeout, 1800));
|
||||
await sleep(Math.min(timeout, Number(body.initialWaitMs || process.env.BACNET_INITIAL_WAIT_MS || 4500)));
|
||||
|
||||
const hosts = routing.mode === "broadcast" ? buildUnicastScanHosts(body, routing) : [];
|
||||
const needsUnicastMapping = routing.mode === "broadcast" && hosts.length && (!devices.length || devices.some((device) => !hosts.includes(device.address)));
|
||||
@@ -721,7 +751,7 @@ async function scanNetwork(body) {
|
||||
}
|
||||
}
|
||||
currentUnicastTarget = "";
|
||||
await sleep(250);
|
||||
await sleep(Number(body.finalWaitMs || process.env.BACNET_FINAL_WAIT_MS || 1200));
|
||||
}
|
||||
|
||||
let visibleDevices = preferRealBacnetAddresses(devices);
|
||||
@@ -864,3 +894,13 @@ server.listen(port, () => {
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user