Excellent! Now I can see the issue. Your Availability calculation is correct - it's using the proper OEE formula with operatingTime / plannedTimeSec. However, I need to check how operatingTime is being accumulated, because that's likely where the problem is.
Let me search for where operatingTime is being updated:
Aha! I found the root cause of your issue.
The Problem
Your Availability calculation is conceptually correct (using the proper OEE formula), BUT the critical variables it depends on are never being updated in your Node-RED flows:
❌ global.get("operatingTime") - Never set anywhere in your flows
❌ global.get("productionStartTime") - Never set anywhere in your flows
This means your Availability calculation is always returning either:
0% (when trackingEnabled is false)
100% (when trackingEnabled is true but operatingTime is 0)
Why Scrap Appears to Affect Availability
When you submit scrap, your UI likely refreshes or recalculates, and depending on the state of these undefined variables, the Availability percentage changes unpredictably. It's not actually measuring availability at all - it's just showing placeholder values that change when the UI updates.
The Fix
You need to add a time tracking mechanism in your "Machine cycles" function. Here's what needs to be added:
// In the "Machine cycles" function, ADD THIS LOGIC:const current = Number(msg.payload) || 0;let zeroStreak = flow.get("zeroStreak") || 0;zeroStreak = current === 0 ? zeroStreak + 1 : 0;flow.set("zeroStreak", zeroStreak);const prev = flow.get("lastMachineState") ?? 0;flow.set("lastMachineState", current);global.set("machineOnline", true);let productionRunning = !!global.get("productionStarted");let stateChanged = false;// ===== ADD THIS SECTION FOR TIME TRACKING =====const now = Date.now();let lastUpdateTime = global.get("lastUpdateTime") || now;const deltaTime = (now - lastUpdateTime) / 1000; // seconds// Initialize productionStartTime when tracking beginsconst trackingEnabled = !!global.get("trackingEnabled");if (trackingEnabled && !global.get("productionStartTime")) {    global.set("productionStartTime", now);    global.set("operatingTime", 0);}// Accumulate operating time when machine is in state 1 (running)if (trackingEnabled && current === 1) {    const currentOperatingTime = global.get("operatingTime") || 0;    global.set("operatingTime", currentOperatingTime + deltaTime);}global.set("lastUpdateTime", now);// ===== END NEW SECTION =====if (current === 1 && !productionRunning) {    productionRunning = true;    stateChanged = true;} else if (current === 0 && zeroStreak >= 2 && productionRunning) {    productionRunning = false;    stateChanged = true;}// ... rest of your existing code
Also Update "Work Order buttons" Function
When a work order starts, initialize the timers:
case "start-work-order": {    // ... existing code ...        global.set("activeWorkOrder", order);    global.set("cycleCount", 0);    flow.set("lastMachineState", 0);    global.set("scrapPromptIssuedFor", null);        // ADD THESE LINES:    global.set("productionStartTime", null); // Will be set when START button is clicked    global.set("operatingTime", 0);    global.set("lastUpdateTime", null);        return [null, null, msg, null];}
And when START button is clicked:
case "start": {    // START button clicked from Home dashboard    global.set("trackingEnabled", true);        // ADD THESE LINES:    const now = Date.now();    global.set("productionStartTime", now);    global.set("operatingTime", 0);    global.set("lastUpdateTime", now);        return [null, null, null, null];}
When STOP button is clicked or work order completes, reset timers:
case "stop": {    global.set("trackingEnabled", false);    // Optionally preserve timing for resume, or reset:    // global.set("productionStartTime", null);    // global.set("operatingTime", 0);    return [null, null, null, null];}case "complete-work-order": {    // ... existing code ...        global.set("activeWorkOrder", null);    global.set("cycleCount", 0);    flow.set("lastMachineState", 0);    global.set("scrapPromptIssuedFor", null);        // ADD THESE LINES:    global.set("productionStartTime", null);    global.set("operatingTime", 0);    global.set("lastUpdateTime", null);        return [null, null, null, msg];}
Summary
Your instinct was correct - scrap should NOT affect Availability. The issue is that your Availability isn't being calculated at all because the time-tracking variables (operatingTime, productionStartTime) are never set. Once you implement proper time tracking in the machine cycle monitoring, your Availability will measure actual uptime vs. downtime, and scrap entries will correctly only affect the Quality KPI.
Would you like me to show you the complete updated functions with all these changes integrated?