Utility Bill Optimization Tool

Configure Your Utilities

Set up the utilities you want to track. This information will be saved in your browser.

Enter Utility Bill Data

Entered Bills for

No bills entered for this utility yet.

Analyze Consumption & Cost Trends

Select a configured utility with bill data to see analysis.

Optimization Scenarios & Estimator

Select a configured utility to explore scenarios. Appliance estimator is available for Electricity if configured.

Download Your Utility Report

The PDF report will include your utility configurations, detailed bill entries, trend analysis summaries (data and charts if possible), current "What-If" scenario results, and appliance estimations (if available).

Scenario: Reduce Consumption by ${reductionPercent}%
Estimated New Avg. Consumption: ${reducedConsumption.toFixed(2)} ${config.unit}
Estimated New Avg. Monthly Bill: $${newBillFromReduction.toFixed(2)}
Estimated Monthly Savings: $${savingsFromReduction.toFixed(2)} (Annual: $${(savingsFromReduction*12).toFixed(2)})


`; } const newRate = parseFloat(whatIfNewRateEl.value); if (!isNaN(newRate) && newRate >= 0) { const newBillFromRateChange = (avgConsumption * newRate) + config.fixedCharge; const savingsFromRateChange = currentAvgBill - newBillFromRateChange; resultsHtml += `

Scenario: Rate Change to $${newRate.toFixed(4)} per ${config.unit}
Estimated New Avg. Monthly Bill: $${newBillFromRateChange.toFixed(2)}
Estimated Monthly ${savingsFromRateChange >= 0 ? 'Savings' : 'Increase'}: $${Math.abs(savingsFromRateChange).toFixed(2)}

`; } if (resultsHtml) { if(whatIfResultsDisplayEl) whatIfResultsDisplayEl.innerHTML = resultsHtml; if(whatIfResultsDisplayEl) whatIfResultsDisplayEl.style.display = 'block'; } else { if(whatIfResultsDisplayEl) whatIfResultsDisplayEl.innerHTML = '

Enter a value for reduction or new rate to see scenario results.

'; if(whatIfResultsDisplayEl) whatIfResultsDisplayEl.style.display = 'block'; } } window.ubot_addAppliance = function() { const name = applianceNameEl.value.trim(); const wattage = parseFloat(applianceWattageEl.value); const hours = parseFloat(applianceHoursPerDayEl.value); if (!name || isNaN(wattage) || wattage <=0 || isNaN(hours) || hours < 0 || hours > 24) { alert("Please enter valid appliance name, positive wattage, and hours (0-24)."); return; } applianceEstimates.push({ id: Date.now(), name, wattage, hours }); saveData(); ubot_renderAppliancesTable(); applianceNameEl.value = ''; applianceWattageEl.value = ''; applianceHoursPerDayEl.value = ''; } function ubot_deleteAppliance(applianceId) { // Not global as it's called by a JS-created handler applianceEstimates = applianceEstimates.filter(app => app.id !== applianceId); saveData(); ubot_renderAppliancesTable(); } function ubot_renderAppliancesTable() { const elecConfig = utilityConfigs["Electricity"]; if (!elecConfig || typeof elecConfig.rate !== 'number') { // Check if rate is a number if(appliancesTableEl) appliancesTableEl.style.display = 'none'; if (appliancePieChartInstance) appliancePieChartInstance.destroy(); appliancePieChartInstance = null; return; } if (applianceEstimates.length === 0) { if(appliancesTableEl) appliancesTableEl.style.display = 'none'; if (appliancePieChartInstance) appliancePieChartInstance.destroy(); appliancePieChartInstance = null; return; } if(appliancesTableEl) appliancesTableEl.style.display = 'table'; if(appliancesTableBodyEl) appliancesTableBodyEl.innerHTML = ''; let pieChartLabels = []; let pieChartData = []; applianceEstimates.forEach(app => { const dailyKWh = (app.wattage * app.hours) / 1000; const monthlyKWh = dailyKWh * 30; const monthlyCost = monthlyKWh * elecConfig.rate; const row = appliancesTableBodyEl.insertRow(); row.insertCell().textContent = app.name; row.insertCell().textContent = app.wattage; row.insertCell().textContent = app.hours.toFixed(1); row.insertCell().textContent = monthlyKWh.toFixed(2); row.insertCell().textContent = monthlyCost.toFixed(2); const actionsCell = row.insertCell(); const deleteBtn = document.createElement('button'); deleteBtn.textContent = 'Del'; deleteBtn.className = 'ubot-button danger small'; deleteBtn.onclick = () => ubot_deleteAppliance(app.id); actionsCell.appendChild(deleteBtn); pieChartLabels.push(app.name); pieChartData.push(monthlyKWh); }); if (appliancePieChartCanvas) { if (appliancePieChartInstance) appliancePieChartInstance.destroy(); if (pieChartData.length > 0) { appliancePieChartInstance = new Chart(appliancePieChartCanvas.getContext('2d'), { type: 'pie', data: { labels: pieChartLabels, datasets: [{ label: 'Est. Monthly kWh', data: pieChartData, backgroundColor: ['#007bff', '#28a745', '#ffc107', '#dc3545', '#17a2b8', '#6c757d', '#fd7e14', '#6610f2', '#20c997'], hoverOffset: 4 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'top'}}} }); } } else { console.error("Appliance pie chart canvas not found.");} } // --- Tab 5: PDF Export --- if(downloadPdfButton) downloadPdfButton.addEventListener('click', async function() { let pdfHtml = `
`; pdfHtml += `

Utility Bill Optimization Report

`; pdfHtml += `

Generated on: ${new Date().toLocaleDateString()}

`; // ... (rest of PDF generation logic - it's long, assuming it's mostly correct from before, but relies on chart instances being valid) // Crucial: Ensure chart instances are valid and getBase64Image() is called correctly. // Add placeholders if charts can't be generated. pdfHtml += `

Utility Configurations

`; let hasConfigs = false; UTILITY_TYPES.forEach(type => { const config = utilityConfigs[type]; if (config && config.unit) { hasConfigs = true; pdfHtml += `

${type}

  • Unit: ${config.unit}
  • Rate: $${(config.rate || 0).toFixed(4)} / ${config.unit}
  • Fixed Charge: $${(config.fixedCharge || 0).toFixed(2)} / month
`; } }); if (!hasConfigs) pdfHtml += "

No utilities configured.

"; pdfHtml += `
`; pdfHtml += `

Detailed Bill Entries

`; let hasBills = false; UTILITY_TYPES.forEach(type => { const bills = utilityBills[type]; const config = utilityConfigs[type]; if (bills && bills.length > 0 && config && config.unit) { hasBills = true; pdfHtml += `

${type} (Unit: ${config.unit})

`; const sortedBills = [...bills].sort((a,b) => a.period.localeCompare(b.period)); sortedBills.forEach(bill => { const effectiveRate = bill.consumption > 0 ? ((bill.totalBill - (config.fixedCharge || 0)) / bill.consumption) : NaN; pdfHtml += ``; }); pdfHtml += `
PeriodConsumptionTotal Bill ($)Effective Rate ($/${config.unit})
${bill.period}${bill.consumption.toFixed(2)}${bill.totalBill.toFixed(2)}${isNaN(effectiveRate) ? '-' : effectiveRate.toFixed(4)}
`; } }); if (!hasBills) pdfHtml += "

No bill data entered.

"; pdfHtml += `
`; pdfHtml += `

Trend Analysis Summaries

`; let hasAnalysis = false; for (const type of UTILITY_TYPES) { const bills = utilityBills[type]; if (bills && bills.length > 0 && utilityConfigs[type] && utilityConfigs[type].unit) { hasAnalysis = true; pdfHtml += `

${type} Analysis

`; let totalConsumption = 0, totalCost = 0; bills.forEach(b => { totalConsumption += b.consumption; totalCost += b.totalBill; }); const avgConsumption = bills.length > 0 ? totalConsumption / bills.length : 0; const avgCost = bills.length > 0 ? totalCost / bills.length : 0; pdfHtml += `

Avg. Monthly Consumption: ${avgConsumption.toFixed(2)} ${utilityConfigs[type].unit}

`; pdfHtml += `

Avg. Monthly Bill: $${avgCost.toFixed(2)}

`; if (analysisUtilitySelectEl && analysisUtilitySelectEl.value === type) { if (consumptionChartInstance) { try { pdfHtml += `

Consumption Trend

`; } catch (e) {pdfHtml +=`

(Consumption chart image not available)

`} } if (costChartInstance) { try { pdfHtml += `

Cost Trend

`; } catch (e) {pdfHtml +=`

(Cost chart image not available)

`} } } else { pdfHtml += `

(Trend charts for ${type} not included in this snapshot as it was not the active analysis view.)

`; } } } if (!hasAnalysis) pdfHtml += "

No analysis performed.

"; pdfHtml += `
`; pdfHtml += `

"What-If" Scenario Results

`; if (optimizeUtilitySelectEl && optimizeUtilitySelectEl.value && whatIfResultsDisplayEl && whatIfResultsDisplayEl.style.display !== 'none' && whatIfResultsDisplayEl.innerHTML.length > 20) { pdfHtml += `

For Utility: ${optimizeUtilitySelectEl.value}

`; pdfHtml += `
Parameters: Reduction ${whatIfReductionPercentEl.value || 'N/A'}%, New Rate $${whatIfNewRateEl.value || 'N/A'} per ${utilityConfigs[optimizeUtilitySelectEl.value]?.unit || 'unit'}
`; pdfHtml += whatIfResultsDisplayEl.innerHTML; } else { pdfHtml += "

No 'What-If' scenarios calculated or utility not selected.

"; } pdfHtml += `
`; pdfHtml += `

Appliance Electricity Estimator

`; if (applianceEstimates.length > 0 && utilityConfigs["Electricity"] && typeof utilityConfigs["Electricity"].rate === 'number') { const elecRate = utilityConfigs["Electricity"].rate; pdfHtml += ``; applianceEstimates.forEach(app => { const monthlyKWh = (app.wattage * app.hours * 30) / 1000; const monthlyCost = monthlyKWh * elecRate; pdfHtml += ``; }); pdfHtml += `
ApplianceWattsHrs/DayEst. kWh/MonthEst. Cost/Month ($)
${app.name}${app.wattage}${app.hours.toFixed(1)}${monthlyKWh.toFixed(2)}${monthlyCost.toFixed(2)}
`; if (appliancePieChartInstance) { try { pdfHtml += `

Appliance Consumption Share (Est. kWh/Month)

`; } catch (e) { pdfHtml += `

(Appliance chart image not available)

`; } } } else { pdfHtml += "

No appliance data or Electricity rate not configured.

"; } pdfHtml += `
`; pdfHtml += `
`; // Close ubot-pdf-output const pdfOutputEl = toolContainer.querySelector('#ubot-pdfOutput'); if (pdfOutputEl) pdfOutputEl.innerHTML = pdfHtml; const opt = { margin: [0.5, 0.4, 0.5, 0.4], filename: `Utility_Optimization_Report_${new Date().toISOString().slice(0,10)}.pdf`, image: { type: 'jpeg', quality: 0.95 }, html2canvas: { scale: 2, useCORS: true, logging: false, scrollX: 0, scrollY: 0, windowWidth: pdfOutputEl ? pdfOutputEl.scrollWidth : 1200, windowHeight: pdfOutputEl ? pdfOutputEl.scrollHeight + 50 : 1600}, jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }, pagebreak: { mode: ['avoid-all', 'css', 'legacy'] } }; if (html2pdf && pdfOutputEl) { // Check if html2pdf is loaded and pdfOutputEl exists html2pdf().from(pdfOutputEl).set(opt).save() .catch(err => console.error("PDF generation failed:", err)); } else { alert("PDF generation library or output element not ready."); } }); // --- Tab Navigation --- window.ubot_openTab = function(evt, tabName) { tabs.forEach(tab => tab.style.display = 'none'); tabLinks.forEach(link => link.classList.remove('active')); const activeTabContent = toolContainer.querySelector('#' + tabName); if(activeTabContent) activeTabContent.style.display = 'block'; else { console.error(`Tab content for ${tabName} not found.`); return; } let clickedIndex = -1; if (evt && evt.currentTarget) { evt.currentTarget.classList.add('active'); clickedIndex = tabLinks.indexOf(evt.currentTarget); } else { clickedIndex = tabLinks.findIndex(link => { const onclickAttr = link.getAttribute('onclick'); return onclickAttr && onclickAttr.includes(tabName); }); if (clickedIndex !== -1) tabLinks[clickedIndex].classList.add('active'); } currentTabIndex = clickedIndex !== -1 ? clickedIndex : 0; ubot_updateNavButtons(); if (tabName === 'ubot-tab2') { if(billUtilitySelectEl.value) handleBillUtilitySelectChange(); } else if (tabName === 'ubot-tab3') { if(analysisUtilitySelectEl.value) ubot_renderAnalysis(); } else if (tabName === 'ubot-tab4') { if(optimizeUtilitySelectEl.value) ubot_setupOptimizationTab(); } } window.ubot_navigateTab = function(direction) { let newIndex = currentTabIndex; if (direction === 'next' && currentTabIndex < tabs.length - 1) newIndex++; else if (direction === 'prev' && currentTabIndex > 0) newIndex--; if (tabLinks[newIndex] && newIndex !== currentTabIndex) tabLinks[newIndex].click(); } function ubot_updateNavButtons() { if(prevButton) prevButton.disabled = currentTabIndex === 0; if(nextButton) nextButton.disabled = currentTabIndex >= tabs.length - 1; // >= in case tabs array is off } // --- Initialization --- function initializeTool() { if (!Chart || !html2pdf) { alert("Required libraries (Chart.js or html2pdf.js) not loaded. Some features may not work."); // return; // Or continue with degraded functionality } loadData(); renderUtilityConfigs(); populateUtilitySelects(); const today = new Date(); if(billPeriodEl) billPeriodEl.value = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`; ubot_openTab(null, 'ubot-tab1'); } // Defensive check for Chart.js and html2pdf.js (if loaded via CDN, they might take time) let attempts = 0; function checkLibrariesAndInit() { if (typeof Chart !== 'undefined' && typeof html2pdf !== 'undefined') { initializeTool(); } else if (attempts < 20) { // Try for 2 seconds attempts++; setTimeout(checkLibrariesAndInit, 100); } else { alert("Error: Essential libraries (Chart.js, html2pdf.js) could not be loaded. Please check your internet connection or try again later. The tool may not function correctly."); // Fallback initialization without charts or PDF perhaps, or just show an error. // For now, it will proceed but chart/pdf functions will fail more gracefully due to checks. initializeTool(); // Try to init anyway, functions have internal checks } } checkLibrariesAndInit(); })();
Scroll to Top