mirror of
				https://github.com/searxng/searxng.git
				synced 2025-11-04 03:27:06 -05:00 
			
		
		
		
	Author Notes - wttr.in provides 8 hourly time forecasts per day, I assumed that they're always describing the weather for 3 hours each, starting at 1 o'clock in the morning related: - https://github.com/searxng/searxng/pull/4663 - https://github.com/searxng/searxng/issues/4885
		
			
				
	
	
		
			125 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# SPDX-License-Identifier: AGPL-3.0-or-later
 | 
						|
"""wttr.in (weather forecast service)"""
 | 
						|
 | 
						|
from urllib.parse import quote
 | 
						|
from datetime import datetime
 | 
						|
 | 
						|
from searx.result_types import EngineResults, WeatherAnswer
 | 
						|
from searx import weather
 | 
						|
 | 
						|
about = {
 | 
						|
    "website": "https://wttr.in",
 | 
						|
    "wikidata_id": "Q107586666",
 | 
						|
    "official_api_documentation": "https://github.com/chubin/wttr.in#json-output",
 | 
						|
    "use_official_api": True,
 | 
						|
    "require_api_key": False,
 | 
						|
    "results": "JSON",
 | 
						|
}
 | 
						|
 | 
						|
categories = ["weather"]
 | 
						|
 | 
						|
url = "https://wttr.in/{query}?format=j1&lang={lang}"
 | 
						|
 | 
						|
# adapted from https://github.com/chubin/wttr.in/blob/master/lib/constants.py
 | 
						|
WWO_TO_CONDITION: dict[str, weather.WeatherConditionType] = {
 | 
						|
    "113": "clear sky",
 | 
						|
    "116": "partly cloudy",
 | 
						|
    "119": "cloudy",
 | 
						|
    "122": "fair",
 | 
						|
    "143": "fair",
 | 
						|
    "176": "light rain showers",
 | 
						|
    "179": "light snow showers",
 | 
						|
    "182": "light sleet showers",
 | 
						|
    "185": "light sleet",
 | 
						|
    "200": "rain and thunder",
 | 
						|
    "227": "light snow",
 | 
						|
    "230": "heavy snow",
 | 
						|
    "248": "fog",
 | 
						|
    "260": "fog",
 | 
						|
    "263": "light rain showers",
 | 
						|
    "266": "light rain showers",
 | 
						|
    "281": "light sleet showers",
 | 
						|
    "284": "light snow showers",
 | 
						|
    "293": "light rain showers",
 | 
						|
    "296": "light rain",
 | 
						|
    "299": "rain showers",
 | 
						|
    "302": "rain",
 | 
						|
    "305": "heavy rain showers",
 | 
						|
    "308": "heavy rain",
 | 
						|
    "311": "light sleet",
 | 
						|
    "314": "sleet",
 | 
						|
    "317": "light sleet",
 | 
						|
    "320": "heavy sleet",
 | 
						|
    "323": "light snow showers",
 | 
						|
    "326": "light snow showers",
 | 
						|
    "329": "heavy snow showers",
 | 
						|
    "332": "heavy snow",
 | 
						|
    "335": "heavy snow showers",
 | 
						|
    "338": "heavy snow",
 | 
						|
    "350": "light sleet",
 | 
						|
    "353": "light rain showers",
 | 
						|
    "356": "heavy rain showers",
 | 
						|
    "359": "heavy rain",
 | 
						|
    "362": "light sleet showers",
 | 
						|
    "365": "sleet showers",
 | 
						|
    "368": "light snow showers",
 | 
						|
    "371": "heavy snow showers",
 | 
						|
    "374": "light sleet showers",
 | 
						|
    "377": "heavy sleet",
 | 
						|
    "386": "rain showers and thunder",
 | 
						|
    "389": "heavy rain showers and thunder",
 | 
						|
    "392": "snow showers and thunder",
 | 
						|
    "395": "heavy snow showers",
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
def request(query, params):
 | 
						|
    params["url"] = url.format(query=quote(query), lang=params["language"])
 | 
						|
    params["raise_for_httperror"] = False
 | 
						|
 | 
						|
    return params
 | 
						|
 | 
						|
 | 
						|
def _weather_data(location: weather.GeoLocation, data: dict):
 | 
						|
    # the naming between different data objects is inconsitent, thus temp_C and
 | 
						|
    # tempC are possible
 | 
						|
    tempC: float = data.get("temp_C") or data.get("tempC")  # type: ignore
 | 
						|
 | 
						|
    return WeatherAnswer.Item(
 | 
						|
        location=location,
 | 
						|
        temperature=weather.Temperature(unit="°C", value=tempC),
 | 
						|
        condition=WWO_TO_CONDITION[data["weatherCode"]],
 | 
						|
        feels_like=weather.Temperature(unit="°C", value=data["FeelsLikeC"]),
 | 
						|
        wind_from=weather.Compass(int(data["winddirDegree"])),
 | 
						|
        wind_speed=weather.WindSpeed(data["windspeedKmph"], unit="km/h"),
 | 
						|
        pressure=weather.Pressure(data["pressure"], unit="hPa"),
 | 
						|
        humidity=weather.RelativeHumidity(data["humidity"]),
 | 
						|
        cloud_cover=data["cloudcover"],
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def response(resp):
 | 
						|
    res = EngineResults()
 | 
						|
 | 
						|
    if resp.status_code == 404:
 | 
						|
        return res
 | 
						|
 | 
						|
    json_data = resp.json()
 | 
						|
    geoloc = weather.GeoLocation.by_query(resp.search_params["query"])
 | 
						|
 | 
						|
    weather_answer = WeatherAnswer(
 | 
						|
        current=_weather_data(geoloc, json_data["current_condition"][0]),
 | 
						|
        service="wttr.in",
 | 
						|
    )
 | 
						|
 | 
						|
    for day in json_data["weather"]:
 | 
						|
        date = datetime.fromisoformat(day["date"])
 | 
						|
        time_slot_len = 24 // len(day["hourly"])
 | 
						|
        for index, forecast in enumerate(day["hourly"]):
 | 
						|
            forecast_data = _weather_data(geoloc, forecast)
 | 
						|
            forecast_data.datetime = weather.DateTime(date.replace(hour=index * time_slot_len + 1))
 | 
						|
            weather_answer.forecasts.append(forecast_data)
 | 
						|
 | 
						|
    res.add(weather_answer)
 | 
						|
    return res
 |