# 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 = { "113": "clear sky", "116": "partly cloudy", "119": "cloudy", "122": "very cloudy", "143": "fog", "176": "light showers", "179": "light sleet showers", "182": "light sleet", "185": "light sleet", "200": "rain and thunder", "227": "light snow", "230": "heavy snow", "248": "fog", "260": "fog", "263": "light showers", "266": "light rain", "281": "light sleet", "284": "light sleet", "293": "light rain", "296": "light rain", "299": "heavy showers", "302": "heavy rain", "305": "heavy showers", "308": "heavy rain", "311": "light sleet", "314": "light sleet", "317": "light sleet", "320": "light snow", "323": "light snow showers", "326": "light snow showers", "329": "heavy snow", "332": "heavy snow", "335": "heavy snow showers", "338": "heavy snow", "350": "light sleet", "353": "light showers", "356": "heavy showers", "359": "heavy rain", "362": "light sleet showers", "365": "light sleet showers", "368": "light snow showers", "371": "heavy snow showers", "374": "light sleet showers", "377": "light 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): return WeatherAnswer.Item( location=location, # the naming between different data objects is inconsitent, thus temp_C and tempC are possible temperature=weather.Temperature(unit="°C", value=data.get("temp_C") or data.get("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() if len(json_data.get("nearest_area", [])) == 0: return res # no matching location found location = json_data["nearest_area"][0] geoloc = weather.GeoLocation( name=location["areaName"][0]["value"], longitude=float(location["longitude"]), latitude=float(location["latitude"]), elevation="", country_code="", timezone="", ) 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