I built a web app that predicts how much energy solar panels can produce based on real weather data and AI predictions. Here's how I did it!
What Does It Do?
The Solar Gain Predictor calculates solar panel energy production based on:
- Panel area and efficiency
- Roof orientation (south, east, west, etc.)
- Real weather data from your location
- AI predictions trained on 6 months of historical data
Tech Stack
- React + Vite - Fast development
- Tailwind CSS - Beautiful design
- Chart.js - Interactive charts
- TensorFlow.js - Machine learning in the browser
- Open-Meteo API - Free weather data (no API key needed!)
- OpenWeatherMap API - City search
The Weather Data Challenge
I needed real weather data but didn't want to pay for APIs. I found Open-Meteo API which is completely free and provides:
- 180 days of historical data
- 7-day forecasts
- Sunshine duration, cloud coverage, temperature
- No API key required!
For city search, I use OpenWeatherMap's Geocoding API which also returns localized city names (Praha → Prague).
AI Predictions with TensorFlow.js
The coolest feature is AI predictions. Here's how it works:
Training Data
// Get 180 days of historical weather
const historicalData = await getHistoricalWeather(lat, lon, 180);
// Each day contains: month, dayOfYear, temperature, cloudiness, sunHours
Model Architecture
const model = tf.sequential({
layers: [
tf.layers.dense({ units: 64, activation: 'relu', inputShape: [4] }),
tf.layers.dropout({ rate: 0.2 }),
tf.layers.dense({ units: 32, activation: 'relu' }),
tf.layers.dropout({ rate: 0.2 }),
tf.layers.dense({ units: 16, activation: 'relu' }),
tf.layers.dense({ units: 1, activation: 'linear' })
]
});
The model learns patterns between weather conditions and sun hours. It then predicts the next 5 days and compares with actual forecasts!
Internationalization (i18n)
I wanted both Czech and English speakers to use the app, so I added full i18n support:
const LanguageContext = createContext();
export function LanguageProvider({ children }) {
const [language, setLanguage] = useState(
localStorage.getItem('language') || 'cs'
);
const t = (key) => translations[language][key] || key;
return (
<LanguageContext.Provider value={{ language, setLanguage, t }}>
{children}
</LanguageContext.Provider>
);
}
Even city names are translated! When you search "Praha" in English mode, it shows "Prague, CZ".
Rate Limiting for API Protection
Since I use free APIs, I needed to prevent abuse. I added a simple rate limiter:
// Max 10 requests per minute
const rateLimitMap = new Map();
const RATE_LIMIT_MAX_REQUESTS = 10;
const RATE_LIMIT_WINDOW_MS = 60000;
function checkRateLimit(key = 'global') {
const now = Date.now();
const userRequests = rateLimitMap.get(key) || [];
const recentRequests = userRequests.filter(
timestamp => now - timestamp < RATE_LIMIT_WINDOW_MS
);
if (recentRequests.length >= RATE_LIMIT_MAX_REQUESTS) {
return false; // Rate limited
}
recentRequests.push(now);
rateLimitMap.set(key, recentRequests);
return true;
}
Energy Calculation
The calculation is simple:
energy (Wh) = area (m²) × efficiency × sun_hours × orientation_factor × 1000
Orientation factors:
- South: 1.0 (100% - optimal)
- Southeast/Southwest: 0.9 (90%)
- East/West: 0.75 (75%)
- North: 0.5 (50%)
Key Features
- Geolocation - One-click location detection
- City autocomplete - Smart search with 300ms debouncing
- Responsive design - Works on mobile and desktop
- Error handling - Fallback to simulated data
- Beautiful UI - Glassmorphism effects with Tailwind CSS
What I Learned
This project taught me:
- Working with real APIs and handling rate limits
- Machine learning in the browser with TensorFlow.js
- Building a bilingual app (harder than expected!)
- Performance optimization (debouncing, rate limiting)
Source Code
Check out the project on GitHub: https://github.com/TomasDevs/solar-gain-predictor
What's Next?
Future ideas:
- More weather APIs for better accuracy
- Export predictions to PDF
- Historical comparison (this year vs last year)
- Support for multiple panel configurations
What features would you add? Let me know in the comments!
Top comments (0)