Setup Equipment Types & Groups

Manage Individual Equipment Types

Defined Equipment Types:

    Define Equipment Groups

    Defined Groups:

      Define Operational Day & Group Usage

      Define Daily Time Slots

      Defined Time Slots (Must cover 24h without gaps/overlaps):

        Set Group Usage per Time Slot

        For each equipment group, specify the percentage (%) of its full power it typically draws during each defined time slot. (e.g., 100% if fully active, 10% for standby, 0% if off).

        Energy Flow Analysis & Report

        Rates & Operational Settings

        $

        Please define equipment groups and time slots first.

        '; return; } // Rebuild header for detailed table const dhr = detailedHead.insertRow(); dhr.insertCell().textContent = 'Group'; oef_timeSlots.forEach(slot => dhr.insertCell().outerHTML = `${slot.name} (kWh)`); dhr.insertCell().outerHTML = 'Total Daily (kWh)'; dhr.insertCell().outerHTML = 'Daily Cost ($)'; let overallDailyKwh = 0; const energyBySlot = {}; oef_timeSlots.forEach(s => energyBySlot[s.id] = 0); const energyByGroupDaily = {}; oef_equipmentGroups.forEach(g => energyByGroupDaily[g.id] = {name: g.name, totalKwh:0}); oef_equipmentGroups.forEach(group => { const groupRow = detailedBody.insertRow(); groupRow.insertCell().textContent = group.name; let groupDailyKwh = 0; const groupTotalWattage = (group.items || []).reduce((sum, item) => { const eqType = oef_equipmentTypes.find(et => et.id === item.eqTypeId); return sum + (eqType ? eqType.wattage * item.quantity : 0); },0); oef_timeSlots.forEach(slot => { const usagePercent = (oef_groupUsagePatterns[group.id] && oef_groupUsagePatterns[group.id][slot.id] !== undefined) ? oef_groupUsagePatterns[group.id][slot.id] : 0; const slotKwh = (groupTotalWattage / 1000) * (usagePercent / 100) * slot.durationHours; groupRow.insertCell().textContent = slotKwh.toFixed(3); groupDailyKwh += slotKwh; energyBySlot[slot.id] += slotKwh; }); overallDailyKwh += groupDailyKwh; energyByGroupDaily[group.id].totalKwh = groupDailyKwh; groupRow.insertCell().textContent = groupDailyKwh.toFixed(3); groupRow.insertCell().textContent = (groupDailyKwh * oef_settings.electricityCostPerKwh).toFixed(2); }); // Overall Totals Display const dailyCostTotal = overallDailyKwh * oef_settings.electricityCostPerKwh; const monthlyKwhTotal = overallDailyKwh * oef_settings.workdaysPerMonth; const monthlyCostTotal = dailyCostTotal * oef_settings.workdaysPerMonth; const annualKwhTotal = monthlyKwhTotal * 12; const annualCostTotal = monthlyCostTotal * 12; const annualCo2Total = oef_settings.co2IntensityFactor > 0 ? annualKwhTotal * oef_settings.co2IntensityFactor : 0; document.getElementById('oef-totalDailyKwh').textContent = `${overallDailyKwh.toFixed(2)} kWh`; document.getElementById('oef-totalDailyCost').textContent = `$${dailyCostTotal.toFixed(2)}`; document.getElementById('oef-totalMonthlyKwh').textContent = `${monthlyKwhTotal.toFixed(2)} kWh`; document.getElementById('oef-totalMonthlyCost').textContent = `$${monthlyCostTotal.toFixed(2)}`; document.getElementById('oef-totalAnnualKwh').textContent = `${annualKwhTotal.toFixed(2)} kWh`; document.getElementById('oef-totalAnnualCost').textContent = `$${annualCostTotal.toFixed(2)}`; const co2SummaryEl = document.getElementById('oef-co2SummaryLine'); if (oef_settings.co2IntensityFactor > 0) { document.getElementById('oef-totalAnnualCo2').textContent = `${annualCo2Total.toFixed(2)} kg CO2e`; co2SummaryEl.style.display = 'block'; } else { co2SummaryEl.style.display = 'none';} // Charts const flowCtx = document.getElementById('oef-dailyFlowChart').getContext('2d'); const groupPieCtx = document.getElementById('oef-groupBreakdownPieChart').getContext('2d'); const slotLabels = oef_timeSlots.map(s => `${s.name} (${s.startTime}-${s.endTime})`); const slotEnergyData = oef_timeSlots.map(s => energyBySlot[s.id].toFixed(2)); const slotColors = oef_timeSlots.map((_, i) => `hsl(${(i * 360 / (oef_timeSlots.length||1)) % 360}, 65%, 60%)`); if(oef_charts.dailyFlow) oef_charts.dailyFlow.destroy(); if(slotEnergyData.some(d => d > 0)) { oef_charts.dailyFlow = new Chart(flowCtx, { type: 'bar', data: { labels: slotLabels, datasets: [{ label: 'kWh per Slot', data: slotEnergyData, backgroundColor: slotColors[1] || '#2ECC71'}]}, // Use a consistent color or map options: { responsive: true, maintainAspectRatio: false, scales: {y:{beginAtZero:true, title:{display:true, text:'kWh'}}}} }); } else {flowCtx.clearRect(0,0,flowCtx.canvas.width,flowCtx.canvas.height); flowCtx.textAlign="center"; flowCtx.fillText("No flow data.", flowCtx.canvas.width/2, flowCtx.canvas.height/2);} const groupLabels = Object.values(energyByGroupDaily).filter(g=>g.totalKwh > 0).map(g => g.name); const groupEnergyData = Object.values(energyByGroupDaily).filter(g=>g.totalKwh > 0).map(g => g.totalKwh.toFixed(2)); const groupColors = groupLabels.map((_, i) => `hsl(${(i * 360 / (groupLabels.length||1) + 60) % 360}, 75%, 55%)`); if(oef_charts.groupBreakdownPie) oef_charts.groupBreakdownPie.destroy(); if(groupEnergyData.length > 0) { oef_charts.groupBreakdownPie = new Chart(groupPieCtx, { type: 'pie', data: { labels: groupLabels, datasets: [{data: groupEnergyData, backgroundColor: groupColors}]}, options: { responsive: true, maintainAspectRatio: false, plugins: {legend:{position:'right'}, tooltip:{callbacks:{label: (ctx) => `${ctx.label}: ${ctx.raw} kWh (${((ctx.raw/overallDailyKwh)*100).toFixed(1)}%)`}}}} }); } else {groupPieCtx.clearRect(0,0,groupPieCtx.canvas.width,groupPieCtx.canvas.height); groupPieCtx.textAlign="center"; groupPieCtx.fillText("No group data.", groupPieCtx.canvas.width/2, groupPieCtx.canvas.height/2);} } // --- PDF Download --- document.getElementById('oef-downloadPdfBtn').onclick = async () => { if (oef_equipmentGroups.length === 0 || oef_timeSlots.length === 0 || document.getElementById('oef-analysisReportArea').style.display === 'none') { alert("Please setup equipment, time slots, and generate an analysis first."); return; } const { jsPDF } = window.jspdf; const pdf = new jsPDF('p', 'mm', 'a4'); // Portrait for this report let currentY = 15; const margin = 15; const contentWidth = pdf.internal.pageSize.getWidth() - 2*margin; const accentColor = '#2ECC71'; const textColor = '#1D5E3B'; pdf.setFontSize(18); pdf.setTextColor(accentColor); pdf.text("Office Energy Flow Analysis Report", pdf.internal.pageSize.getWidth() / 2, currentY, { align: 'center' }); currentY += 8; pdf.setFontSize(10); pdf.setTextColor(textColor); pdf.text(`Report Generated: ${OEF_TODAY_CONTEXT.toLocaleDateString()}`, pdf.internal.pageSize.getWidth() / 2, currentY, { align: 'center'}); currentY += 6; pdf.text(`Settings: ${oef_settings.workdaysPerMonth} workdays/month | $${oef_settings.electricityCostPerKwh.toFixed(3)}/kWh ${oef_settings.co2IntensityFactor > 0 ? `| ${oef_settings.co2IntensityFactor} kgCO2e/kWh` : ''}`, margin, currentY); currentY += 10; // Overall Totals pdf.setFontSize(12); pdf.setTextColor(accentColor); pdf.text("Overall Estimated Totals (Typical Day):", margin, currentY); currentY += 6; pdf.setFontSize(10); pdf.setTextColor(textColor); pdf.text(`- Total Daily Energy: ${document.getElementById('oef-totalDailyKwh').textContent}`, margin + 5, currentY); currentY += 5; pdf.text(`- Est. Daily Cost: ${document.getElementById('oef-totalDailyCost').textContent}`, margin + 5, currentY); currentY += 7; pdf.text(`Est. Monthly: ${document.getElementById('oef-totalMonthlyKwh').textContent} | ${document.getElementById('oef-totalMonthlyCost').textContent}`, margin +5, currentY); currentY += 5; pdf.text(`Est. Annual: ${document.getElementById('oef-totalAnnualKwh').textContent} | ${document.getElementById('oef-totalAnnualCost').textContent}`, margin+5, currentY); currentY += 5; if (oef_settings.co2IntensityFactor > 0) { pdf.text(`- Est. Annual CO2 Emissions: ${document.getElementById('oef-totalAnnualCo2').textContent}`, margin + 5, currentY); currentY += 5; } currentY += 5; // Charts const chartsToCapture = [ { canvasId: 'oef-dailyFlowChart', title: "Daily Energy Flow by Time Slot (kWh)" }, { canvasId: 'oef-groupBreakdownPieChart', title: "Daily Energy Consumption by Group (%)" } ]; for (const chartInfo of chartsToCapture) { if (currentY > pdf.internal.pageSize.getHeight() - 65 ) { pdf.addPage(); currentY = margin; } const chartCanvas = document.getElementById(chartInfo.canvasId); const chartInstance = chartInfo.canvasId === 'oef-dailyFlowChart' ? oef_charts.dailyFlow : oef_charts.groupBreakdownPie; if (chartInstance && chartInstance.data && chartInstance.data.datasets[0].data.some(d=>parseFloat(d)>0)) { pdf.setFontSize(11); pdf.setTextColor(accentColor); pdf.text(chartInfo.title + ":", margin, currentY); currentY += 5; try { const chartImgData = chartCanvas.toDataURL('image/png', 1.0); const imgProps = pdf.getImageProperties(chartImgData); let pdfImgWidth = contentWidth * 0.8; let pdfImgHeight = (imgProps.height * pdfImgWidth) / imgProps.width; const maxChartHeight = 60; if (pdfImgHeight > maxChartHeight) { pdfImgHeight = maxChartHeight; pdfImgWidth = (imgProps.width * pdfImgHeight) / imgProps.height; } if (pdfImgWidth > contentWidth) { pdfImgWidth = contentWidth; pdfImgHeight = (imgProps.height * pdfImgWidth) / imgProps.width; if(pdfImgHeight > maxChartHeight) pdfImgHeight = maxChartHeight;} pdf.addImage(chartImgData, 'PNG', margin + (contentWidth - pdfImgWidth)/2 , currentY, pdfImgWidth, pdfImgHeight, undefined, 'FAST'); currentY += pdfImgHeight + 7; } catch(e) { console.error("PDF chart error for " + chartInfo.title, e); currentY +=5; } } } // Detailed Breakdown Table for PDF if (currentY > pdf.internal.pageSize.getHeight() - 50) { pdf.addPage(); currentY = margin; } pdf.setFontSize(12); pdf.setTextColor(accentColor); pdf.text("Detailed Consumption (kWh) per Group & Slot:", margin, currentY); currentY += 7; const tableHead = [['Group']]; oef_timeSlots.forEach(slot => tableHead[0].push(`${slot.name.substring(0,10)}.. kWh`)); // Abbreviate for PDF tableHead[0].push('Total kWh/Day'); tableHead[0].push('Cost/Day ($)'); const tableBody = []; const breakdownRows = document.getElementById('oef-detailedFlowTableBody').rows; for(let i=0; i 0) { pdf.autoTable({ head: tableHead, body: tableBody, startY: currentY, theme: 'grid', headStyles: { fillColor: accentColor, textColor: '#FFFFFF', fontSize:7, cellPadding:1 }, styles: { fontSize: 7, cellPadding: 1, overflow:'linebreak' }, columnStyles: { 0:{cellWidth:35}, /* Auto for others or define widths */ } }); } else { pdf.setFontSize(10); pdf.text("No detailed breakdown data available.", margin, currentY); } pdf.save(`Office_Energy_Flow_Report_${OEF_TODAY_CONTEXT.toISOString().slice(0,10)}.pdf`); }; // --- Initial Load --- document.addEventListener('DOMContentLoaded', () => { oef_openTab(null, oef_tabs[0]); const firstTabButton = document.querySelector(`.oef-tab-button[onclick*="'${oef_tabs[0]}'"]`); if (firstTabButton) firstTabButton.classList.add('active'); else console.error("First tab button not found for initial activation."); oef_updateNavButtons(); });
        Scroll to Top