wttr.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. """wttr.in (weather forecast service)"""
  3. from urllib.parse import quote
  4. from datetime import datetime
  5. from searx.result_types import EngineResults, WeatherAnswer
  6. from searx import weather
  7. about = {
  8. "website": "https://wttr.in",
  9. "wikidata_id": "Q107586666",
  10. "official_api_documentation": "https://github.com/chubin/wttr.in#json-output",
  11. "use_official_api": True,
  12. "require_api_key": False,
  13. "results": "JSON",
  14. }
  15. categories = ["weather"]
  16. url = "https://wttr.in/{query}?format=j1&lang={lang}"
  17. # adapted from https://github.com/chubin/wttr.in/blob/master/lib/constants.py
  18. WWO_TO_CONDITION = {
  19. "113": "clear sky",
  20. "116": "partly cloudy",
  21. "119": "cloudy",
  22. "122": "very cloudy",
  23. "143": "fog",
  24. "176": "light showers",
  25. "179": "light sleet showers",
  26. "182": "light sleet",
  27. "185": "light sleet",
  28. "200": "rain and thunder",
  29. "227": "light snow",
  30. "230": "heavy snow",
  31. "248": "fog",
  32. "260": "fog",
  33. "263": "light showers",
  34. "266": "light rain",
  35. "281": "light sleet",
  36. "284": "light sleet",
  37. "293": "light rain",
  38. "296": "light rain",
  39. "299": "heavy showers",
  40. "302": "heavy rain",
  41. "305": "heavy showers",
  42. "308": "heavy rain",
  43. "311": "light sleet",
  44. "314": "light sleet",
  45. "317": "light sleet",
  46. "320": "light snow",
  47. "323": "light snow showers",
  48. "326": "light snow showers",
  49. "329": "heavy snow",
  50. "332": "heavy snow",
  51. "335": "heavy snow showers",
  52. "338": "heavy snow",
  53. "350": "light sleet",
  54. "353": "light showers",
  55. "356": "heavy showers",
  56. "359": "heavy rain",
  57. "362": "light sleet showers",
  58. "365": "light sleet showers",
  59. "368": "light snow showers",
  60. "371": "heavy snow showers",
  61. "374": "light sleet showers",
  62. "377": "light sleet",
  63. "386": "rain showers and thunder",
  64. "389": "heavy rain showers and thunder",
  65. "392": "snow showers and thunder",
  66. "395": "heavy snow showers",
  67. }
  68. def request(query, params):
  69. params["url"] = url.format(query=quote(query), lang=params["language"])
  70. params["raise_for_httperror"] = False
  71. return params
  72. def _weather_data(location: weather.GeoLocation, data: dict):
  73. return WeatherAnswer.Item(
  74. location=location,
  75. # the naming between different data objects is inconsitent, thus temp_C and tempC are possible
  76. temperature=weather.Temperature(unit="°C", value=data.get("temp_C") or data.get("tempC")),
  77. condition=WWO_TO_CONDITION[data["weatherCode"]],
  78. feels_like=weather.Temperature(unit="°C", value=data["FeelsLikeC"]),
  79. wind_from=weather.Compass(int(data["winddirDegree"])),
  80. wind_speed=weather.WindSpeed(data["windspeedKmph"], unit="km/h"),
  81. pressure=weather.Pressure(data["pressure"], unit="hPa"),
  82. humidity=weather.RelativeHumidity(data["humidity"]),
  83. cloud_cover=data["cloudcover"],
  84. )
  85. def response(resp):
  86. res = EngineResults()
  87. if resp.status_code == 404:
  88. return res
  89. json_data = resp.json()
  90. if len(json_data.get("nearest_area", [])) == 0:
  91. return res # no matching location found
  92. location = json_data["nearest_area"][0]
  93. geoloc = weather.GeoLocation(
  94. name=location["areaName"][0]["value"],
  95. longitude=float(location["longitude"]),
  96. latitude=float(location["latitude"]),
  97. elevation="",
  98. country_code="",
  99. timezone="",
  100. )
  101. weather_answer = WeatherAnswer(
  102. current=_weather_data(geoloc, json_data["current_condition"][0]),
  103. service="wttr.in",
  104. )
  105. for day in json_data["weather"]:
  106. date = datetime.fromisoformat(day["date"])
  107. time_slot_len = 24 // len(day["hourly"])
  108. for index, forecast in enumerate(day["hourly"]):
  109. forecast_data = _weather_data(geoloc, forecast)
  110. forecast_data.datetime = weather.DateTime(date.replace(hour=index * time_slot_len + 1))
  111. weather_answer.forecasts.append(forecast_data)
  112. res.add(weather_answer)
  113. return res