DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

Building a Weather Widget for Firefox New Tab: API-Free Approach with wttr.in

Building a Weather Widget for Firefox New Tab: API-Free Approach with wttr.in

Most weather widgets require API keys, rate limits, and sometimes credit cards. There's a better way: wttr.in — a free, open, consent-friendly weather service that requires zero authentication.

What is wttr.in?

wttr.in is a console-oriented weather service by Igor Chubin. It supports:

  • Plain text output (for terminals)
  • JSON API output (for apps)
  • Auto-detects location from IP
  • Supports city name, coordinates, airport codes, and more

The JSON API

https://wttr.in/London?format=j1
Enter fullscreen mode Exit fullscreen mode

This returns a full JSON response with current conditions plus a 3-day forecast. No API key. No account. No rate limit (be reasonable).

Parsing the Response

async function fetchWeather(location = '') {
  const url = `https://wttr.in/${encodeURIComponent(location)}?format=j1`;

  const response = await fetch(url);
  if (!response.ok) throw new Error(`Weather fetch failed: ${response.status}`);

  const data = await response.json();
  return parseWeatherData(data);
}

function parseWeatherData(data) {
  const current = data.current_condition[0];
  const today = data.weather[0];
  const tomorrow = data.weather[1];
  const dayAfter = data.weather[2];

  return {
    temperature: {
      c: parseInt(current.temp_C),
      f: parseInt(current.temp_F),
    },
    feelsLike: {
      c: parseInt(current.FeelsLikeC),
      f: parseInt(current.FeelsLikeF),
    },
    humidity: parseInt(current.humidity),
    description: current.weatherDesc[0].value,
    weatherCode: parseInt(current.weatherCode),
    forecast: [
      parseForecastDay(today),
      parseForecastDay(tomorrow),
      parseForecastDay(dayAfter),
    ],
    location: data.nearest_area[0],
  };
}

function parseForecastDay(day) {
  return {
    date: day.date,
    maxC: parseInt(day.maxtempC),
    minC: parseInt(day.mintempC),
    maxF: parseInt(day.maxtempF),
    minF: parseInt(day.mintempF),
    description: day.hourly[4]?.weatherDesc[0]?.value || '',
    weatherCode: parseInt(day.hourly[4]?.weatherCode || 0),
    sunrise: day.astronomy[0]?.sunrise || '',
    sunset: day.astronomy[0]?.sunset || '',
  };
}
Enter fullscreen mode Exit fullscreen mode

Weather Code → Icon Mapping

wttr.in uses standard WMO weather interpretation codes:

function getWeatherIcon(code) {
  const icons = {
    113: '☀️',  // Sunny/Clear
    116: '',  // Partly cloudy
    119: '☁️',  // Cloudy
    122: '☁️',  // Overcast
    143: '🌫️', // Mist
    176: '🌦️', // Patchy rain
    179: '🌨️', // Patchy snow
    182: '🌧️', // Patchy sleet
    185: '🌧️', // Patchy freezing drizzle
    200: '⛈️', // Thundery outbreaks
    227: '🌨️', // Blowing snow
    230: '❄️',  // Blizzard
    248: '🌫️', // Fog
    260: '🌫️', // Freezing fog
    263: '🌦️', // Light drizzle
    266: '🌧️', // Drizzle
    281: '🌧️', // Freezing drizzle
    284: '🌧️', // Heavy freezing drizzle
    293: '🌦️', // Light rain
    296: '🌧️', // Rain
    299: '🌧️', // Moderate rain
    302: '🌧️', // Heavy rain
    305: '🌧️', // Heavy rain at times
    308: '🌧️', // Very heavy rain
    311: '🌧️', // Light sleet
    314: '🌧️', // Sleet
    317: '🌨️', // Light snow/sleet
    320: '🌨️', // Moderate or heavy sleet
    323: '🌨️', // Light snow
    326: '🌨️', // Snow
    329: '❄️',  // Moderate snow
    332: '❄️',  // Heavy snow
    335: '❄️',  // Snow blowing
    338: '❄️',  // Heavy snow
    350: '🌧️', // Ice pellets
    353: '🌦️', // Light rain shower
    356: '🌧️', // Moderate or heavy rain shower
    359: '🌧️', // Torrential rain shower
    362: '🌨️', // Light sleet showers
    365: '🌨️', // Moderate or heavy sleet showers
    368: '🌨️', // Light snow showers
    371: '🌨️', // Moderate or heavy snow showers
    374: '🌨️', // Light showers of ice pellets
    377: '🌨️', // Moderate or heavy showers of ice pellets
    386: '⛈️', // Patchy light rain with thunder
    389: '⛈️', // Moderate or heavy rain with thunder
    392: '⛈️', // Patchy light snow with thunder
    395: '⛈️', // Moderate or heavy snow with thunder
  };
  return icons[code] || '🌡️';
}
Enter fullscreen mode Exit fullscreen mode

Auto-Location Detection

wttr.in detects location from the request IP by default — just omit the location parameter:

// Auto-detect location
const weather = await fetchWeather('');
// or
const weather = await fetchWeather('auto');
Enter fullscreen mode Exit fullscreen mode

For browser extensions, this works great as the fetch goes from the user's IP.

Caching Strategy

Weather doesn't change every second. Cache aggressively:

const CACHE_DURATION_MS = 10 * 60 * 1000; // 10 minutes

async function fetchWeatherCached(location = '') {
  const cacheKey = `weather_${location}`;
  const cached = await browser.storage.local.get(cacheKey);

  if (cached[cacheKey]) {
    const { data, timestamp } = cached[cacheKey];
    if (Date.now() - timestamp < CACHE_DURATION_MS) {
      return data; // Return cached data
    }
  }

  // Fetch fresh data
  const data = await fetchWeather(location);
  await browser.storage.local.set({
    [cacheKey]: { data, timestamp: Date.now() }
  });
  return data;
}
Enter fullscreen mode Exit fullscreen mode

Error Handling

async function fetchWeatherSafe(location = '') {
  try {
    return await fetchWeatherCached(location);
  } catch (error) {
    console.warn('Weather fetch failed:', error);
    // Try to return stale cache rather than showing error
    const cacheKey = `weather_${location}`;
    const cached = await browser.storage.local.get(cacheKey);
    if (cached[cacheKey]) {
      return { ...cached[cacheKey].data, stale: true };
    }
    return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

Why Not OpenWeatherMap?

wttr.in OpenWeatherMap Free Tier
API Key Not required Required
Rate limit Generous (no hard limit) 60 calls/min, 1M/month
Setup friction Zero Account + key management
Privacy IP-based location Account linked
Self-hostable Yes (open source) No

For a privacy-focused extension, wttr.in is a natural fit — no account, no tracking, no API key to manage or expire.

The Result

This approach powers the weather widget in Weather & Clock Dashboard — a free Firefox new tab extension. Zero API keys for users to configure, works immediately after install, and respects privacy.


Weather & Clock Dashboard — live weather + 3-day forecast, world clocks, search bar for your Firefox new tab. Free, no tracking, MIT licensed.

Top comments (0)