diff --git a/README.md b/README.md index 1d53ae7..02c72ee 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file **73 de K0CJH and the OpenHamClock contributors!** -*The original HamClock will cease to function in June 2026. OpenHamClock carries forward Elwood's legacy with modern technology and open-source community development.* +*"The original HamClock will cease to function in June 2026. OpenHamClock carries forward Elwood's legacy with modern technology and open-source community development."* diff --git a/public/index.html b/public/index.html index c0abb4b..d7fcce9 100644 --- a/public/index.html +++ b/public/index.html @@ -813,6 +813,86 @@ return { data, loading }; }; + // ============================================ + // LOCAL WEATHER HOOK - Using Open-Meteo API (free, no API key) + // ============================================ + const useLocalWeather = (location) => { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!location || !location.lat || !location.lon) { + setLoading(false); + return; + } + + const fetchWeather = async () => { + try { + // Open-Meteo free API - no key required + const url = `https://api.open-meteo.com/v1/forecast?latitude=${location.lat}&longitude=${location.lon}¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m,wind_direction_10m&temperature_unit=fahrenheit&wind_speed_unit=mph&timezone=auto`; + + const response = await fetch(url); + if (response.ok) { + const result = await response.json(); + const current = result.current; + + // Weather code to description mapping + const weatherCodes = { + 0: { desc: 'Clear', icon: '☀️' }, + 1: { desc: 'Mostly Clear', icon: '🌤️' }, + 2: { desc: 'Partly Cloudy', icon: '⛅' }, + 3: { desc: 'Overcast', icon: '☁️' }, + 45: { desc: 'Foggy', icon: '🌫️' }, + 48: { desc: 'Rime Fog', icon: '🌫️' }, + 51: { desc: 'Light Drizzle', icon: '🌧️' }, + 53: { desc: 'Drizzle', icon: '🌧️' }, + 55: { desc: 'Heavy Drizzle', icon: '🌧️' }, + 61: { desc: 'Light Rain', icon: '🌧️' }, + 63: { desc: 'Rain', icon: '🌧️' }, + 65: { desc: 'Heavy Rain', icon: '🌧️' }, + 71: { desc: 'Light Snow', icon: '🌨️' }, + 73: { desc: 'Snow', icon: '🌨️' }, + 75: { desc: 'Heavy Snow', icon: '🌨️' }, + 77: { desc: 'Snow Grains', icon: '🌨️' }, + 80: { desc: 'Light Showers', icon: '🌦️' }, + 81: { desc: 'Showers', icon: '🌦️' }, + 82: { desc: 'Heavy Showers', icon: '🌦️' }, + 85: { desc: 'Snow Showers', icon: '🌨️' }, + 86: { desc: 'Heavy Snow Showers', icon: '🌨️' }, + 95: { desc: 'Thunderstorm', icon: '⛈️' }, + 96: { desc: 'Thunderstorm w/ Hail', icon: '⛈️' }, + 99: { desc: 'Severe Thunderstorm', icon: '⛈️' } + }; + + const weather = weatherCodes[current.weather_code] || { desc: 'Unknown', icon: '❓' }; + + setData({ + temp: Math.round(current.temperature_2m), + humidity: current.relative_humidity_2m, + windSpeed: Math.round(current.wind_speed_10m), + windDir: current.wind_direction_10m, + description: weather.desc, + icon: weather.icon + }); + console.log('[Weather] Loaded:', current.temperature_2m + '°F', weather.desc); + } + } catch (err) { + console.error('Weather fetch error:', err); + setData(null); + } finally { + setLoading(false); + } + }; + + fetchWeather(); + // Refresh every 15 minutes + const interval = setInterval(fetchWeather, 900000); + return () => clearInterval(interval); + }, [location.lat, location.lon]); + + return { data, loading }; + }; + // ============================================ // CONTEST CALENDAR HOOK // ============================================ @@ -2230,6 +2310,7 @@ config, currentTime, utcTime, utcDate, localTime, localDate, deGrid, dxGrid, deSunTimes, dxSunTimes, dxLocation, onDXChange, spaceWeather, bandConditions, potaSpots, dxCluster, dxPaths, contests, propagation, mySpots, satellites, + localWeather, use12Hour, onTimeFormatToggle, onSettingsClick, onFullscreenToggle, isFullscreen }) => { const bearing = calculateBearing(config.location.lat, config.location.lon, dxLocation.lat, dxLocation.lon); @@ -2274,7 +2355,7 @@ padding: '2px', overflow: 'hidden' }}> - {/* TOP LEFT - Callsign & Time */} + {/* TOP LEFT - Callsign & Weather */}