Unified weather service APIs with sun/moon data
Project description
Temporalis
Unified weather abstraction library for Python. Query multiple weather services through one consistent API, with automatic field derivation, marine forecasts, and an ensemble mode that merges all free sources in parallel.
Why another weather library?
Most Python weather packages are thin wrappers around a single API. Switch providers and you rewrite your whole application. Temporalis solves a different problem: make the provider an implementation detail.
Every provider — whether it's a global model, a national government API, or a
free open-data feed — returns the same objects: WeatherData, DataPoint,
HourlyForecast, DailyForecast. Your code never touches raw JSON.
The data model earns its keep
DataPoint is not a float. A temperature reading has a value, but it also
has a unit, a min, a max, a probability, and a timestamp. A wind speed has a
unit that differs between providers. DataPoint captures all of that in one
object that serialises cleanly and degrades gracefully when a provider doesn't
supply a field:
temp = wx.weather.temperature
print(temp.value, temp.units) # 18.5 ºC
print(temp.min_val, temp.max_val) # daily range, if the provider supplies it
Missing fields are filled automatically. When a provider doesn't return dew point, apparent temperature, snow, or UV index, Temporalis derives them from whatever data is available using standard meteorological formulas:
wx = MetNo(lat, lon)
print(wx.weather.dewPoint) # derived via August-Roche-Magnus
print(wx.weather.uvIndex) # derived from solar position + cloud cover
print(wx.weather.snow) # derived when T ≤ 2°C and precipitation > 0
Sun and moon are first-class, not bolted on. Every provider exposes dawn,
dusk, sunrise, sunset, noon, moon_phase, and moon_phase_name with no
extra API call — computed from coordinates via astral.
Swap providers without changing your code:
# works identically for OWM, OpenMeteo, MetNo, IPMA, NWS, Ensemble
for day in wx.days:
print(day.weekday, day.temperature, day.precipitation)
Install
pip install temporalis
Providers
| Provider | Coverage | API key | Notes |
|---|---|---|---|
Ensemble |
Global | None (OWM optional) | Merges all applicable sources in parallel |
OpenMeteo |
Global | None | Forecast + historical archive |
MetNo |
Global | None | Norwegian Met Institute |
OWM |
Global | Required | OpenWeatherMap; default key bundled |
NWS |
USA only | None | Raises ValueError outside US |
IPMA |
Portugal only | None | Raises ValueError outside PT |
OpenMeteoMarine |
Ocean | None | Wave, swell, current; raises ValueError for landlocked coords |
OpenMeteoAirQuality |
Global | None | PM2.5, ozone, pollen, NO₂ |
Quick Start
Single provider
from temporalis.providers.openmeteo import OpenMeteo
lat, lon = 38.7223, -9.1393 # Lisbon
wx = OpenMeteo(lat, lon)
print(wx.weather.summary)
print(wx.weather.temperature) # DataPoint: value + units
print(wx.weather.dewPoint) # derived if provider doesn't supply it
for day in wx.days:
print(day.weekday, day.datetime.date(), day.temperature)
for hour in wx.hours:
print(hour.datetime.time(), hour.temperature, hour.precipitation)
# Sun times (astral, timezone-aware)
print(wx.dawn, wx.sunrise, wx.noon, wx.sunset, wx.dusk)
# Moon
print(wx.moon_symbol, wx.moon_phase_name)
Ensemble — best data from all free sources
from temporalis.providers.ensemble import Ensemble
wx = Ensemble(lat, lon, units="metric")
print(wx.providers) # ['openmeteo', 'metno', 'ipma', 'openmeteo_marine', ...]
w = wx.weather
print(w.temperature) # mean across all providers
# min_val / max_val reflect inter-provider spread — wide = low confidence
print(w.temperature.min_val, w.temperature.max_val)
print(w.waveHeight) # from OpenMeteoMarine when coastal
Historical data
from temporalis.providers.openmeteo import OpenMeteo
import pendulum
wx = OpenMeteo(lat, lon,
start=pendulum.date(2024, 1, 1),
end=pendulum.date(2024, 1, 31))
for day in wx.days:
print(day.datetime.date(), day.temperature)
Marine forecast
from temporalis.providers.openmeteo_marine import OpenMeteoMarine
wx = OpenMeteoMarine(38.7, -9.5) # must be over ocean
w = wx.weather
print(w.waveHeight, w.swellHeight, w.wavePeriod)
print(w.currentVelocity, w.currentDirection)
Provider registry
import temporalis.providers.registry # auto-registers all built-ins
from temporalis.providers import WeatherProvider
print(WeatherProvider.available())
# ['ensemble', 'ipma', 'metno', 'nws', 'openmeteo', 'openmeteo_airquality',
# 'openmeteo_marine', 'owm']
wx = WeatherProvider.get("metno", lat, lon)
wx = WeatherProvider.from_address("Paris, France", name="openmeteo")
Geocode from address
wx = OpenMeteo.from_address("Berlin, Germany")
wx = Ensemble.from_address("Oslo, Norway")
Units
Pass units="metric" (default) or units="us" to any provider constructor.
Each provider converts locally — API-native units are never exposed raw.
wx_us = OpenMeteo(lat, lon, units="us")
print(wx_us.weather.temperature) # ºF
print(wx_us.weather.windSpeed) # mph
Caching
Each provider instance owns its own requests.Session. To add caching, wrap
the session after construction:
import requests_cache
wx = OpenMeteo(lat, lon)
wx.session = requests_cache.CachedSession("weather_cache", expire_after=600)
Derived fields
Fields filled automatically when the provider doesn't supply them:
| Field | Formula | Inputs |
|---|---|---|
dewPoint |
August-Roche-Magnus | temp + humidity |
apparentTemperature |
Wind chill (T<10°C) or heat index (T>27°C) | temp + wind or humidity |
snow |
precipitation when T ≤ 2°C | temp + precipitation |
uvIndex |
NOAA solar position + Josefsson cloud attenuation | lat/lon + datetime + cloud cover |
See docs/derived-fields.md for formulas, validity ranges, and accuracy limits.
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file temporalis-0.5.0.tar.gz.
File metadata
- Download URL: temporalis-0.5.0.tar.gz
- Upload date:
- Size: 54.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c736b96eb46e7bcae3ef0d6e985feb97ea480f53eabe65c417e5aee626f7638
|
|
| MD5 |
398b272bb9e77b29433647d3fb9080e1
|
|
| BLAKE2b-256 |
421fa2d5e1ac16098c3a404ad0199caa2833fdb31b0f31ad1cf99774d18a9c6d
|
File details
Details for the file temporalis-0.5.0-py3-none-any.whl.
File metadata
- Download URL: temporalis-0.5.0-py3-none-any.whl
- Upload date:
- Size: 45.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a9c649fb8a38635c5cabc7ce466a9aa1c6f2d5fe503ec2faeff5a2eff53692b
|
|
| MD5 |
edc8a1704ad6d3921e24f862f26a94e9
|
|
| BLAKE2b-256 |
0e3df86c56ae7038e07ff843e9080d3f5e6a4565c6d4ef91f15354c6b86eeee5
|