#!/usr/bin/env python3
import os
import sys
import json
import gi
import requests
from datetime import datetime, timezone
gi.require_version('Geoclue', '2.0')
from gi.repository import Geoclue
# ─────────── ICONOS DEL CLIMA ─────────── #
weather_icons = {
"01d": "", "01n": "", "02d": "", "02n": "",
"03d": "", "03n": "", "04d": "", "04n": "",
"09d": "", "09n": "", "10d": "", "10n": "",
"11d": "", "11n": "", "13d": "", "13n": "",
"50d": "", "50n": "", "default": ""
}
# ─────────── API KEYS ─────────── #
try:
with open('/home/teraflops/apikeys/openweathermaps', 'r') as f:
api_key = f.read().strip()
with open('/home/teraflops/apikeys/google_geoip', 'r') as f:
google_key = f.read().strip()
except Exception:
print(json.dumps({"text": "Error al leer las API keys"}))
sys.exit(1)
# ─────────── OBTENER UBICACIÓN ─────────── #
def get_location_from_geoclue():
try:
clue = Geoclue.Simple.new_sync("weather-script", Geoclue.AccuracyLevel.EXACT, None)
location = clue.get_location()
lat = location.get_property('latitude')
lon = location.get_property('longitude')
desc = location.get_property('description')
acc = location.get_property('accuracy')
print(f"🛰️ GeoClue: {lat}, {lon} — Precisión: {acc:.1f} m — Fuente: {desc}")
return lat, lon, desc
except Exception as e:
print("GeoClue (GI): error al obtener ubicación:", e)
return None
def get_location_from_google():
try:
url = f"https://www.googleapis.com/geolocation/v1/geolocate?key={google_key}"
response = requests.post(url, json={})
response.raise_for_status()
data = response.json()
lat = data['location']['lat']
lon = data['location']['lng']
accuracy = data.get('accuracy', 0)
print(f"🛰️ GoogleGeo: {lat}, {lon} — Precisión: {accuracy:.0f} m")
return lat, lon, f"GoogleGeo ({accuracy:.0f} m)"
except Exception as e:
print("Google Geolocation API: fallo:", e)
return None
def reverse_geocode(lat, lon):
try:
url = "https://nominatim.openstreetmap.org/reverse"
params = {
"lat": lat,
"lon": lon,
"format": "json",
"zoom": 10,
"addressdetails": 1,
}
headers = {"User-Agent": "weather-waybar-script"}
response = requests.get(url, params=params, headers=headers)
response.raise_for_status()
data = response.json()
address = data.get("address", {})
ciudad = address.get("city") or address.get("town") or address.get("village") or "Desconocido"
pais = address.get("country", "Desconocido")
return ciudad, pais
except Exception as e:
print("Reverse geocode falló:", e)
return "Desconocido", "Desconocido"
# ─────────── ALERTA LLUVIA SOLO EN TOOLTIP ─────────── #
def check_rain_alert(forecast_data, threshold=60, max_horas=6):
ahora = datetime.utcnow()
mensaje_alerta = ""
for block in forecast_data['list']:
bloque_hora = datetime.strptime(block['dt_txt'], "%Y-%m-%d %H:%M:%S")
if bloque_hora <= ahora:
continue
if (bloque_hora - ahora).total_seconds() > max_horas * 3600:
continue
pop = block.get('pop', 0) * 100
if pop >= threshold:
hora_local = bloque_hora.replace(tzinfo=timezone.utc).astimezone().strftime("%H:%M")
mensaje_alerta = f"⚠️ Posible lluvia a las {hora_local} ({int(pop)}%)"
break
return mensaje_alerta
# ─────────── PRONÓSTICO EXTENDIDO ─────────── #
def get_daily_forecast(forecast_data):
from collections import defaultdict
dias = defaultdict(list)
for entry in forecast_data['list']:
fecha = entry['dt_txt'].split(' ')[0]
dias[fecha].append(entry)
resumen = []
for i, (dia, bloques) in enumerate(dias.items()):
if i >= 3: break
temps = [b['main']['temp'] for b in bloques]
pops = [b.get('pop', 0) * 100 for b in bloques]
estado = bloques[0]['weather'][0]['description'].capitalize()
resumen.append(f"📅 {dia}: {estado}, {round(min(temps))}–{round(max(temps))}°C, ☔ {round(max(pops))}%")
return "\n".join(resumen)
# ─────────── UBICACIÓN ─────────── #
usar_ubicacion_manual = False # Cambia a True si quieres forzar ubicación
if usar_ubicacion_manual:
latitud, longitud = 40.4168, -3.7038 # Ejemplo: Madrid
region = "Manual"
print(f"🧪 Usando ubicación manual: {latitud}, {longitud}")
else:
loc = get_location_from_geoclue() or get_location_from_google()
if not loc:
print(json.dumps({"text": "Error al obtener la ubicación"}))
sys.exit(1)
latitud, longitud, region = loc
ciudad, pais = reverse_geocode(latitud, longitud)
# ─────────── API WEATHER ─────────── #
params = {
"lat": latitud,
"lon": longitud,
"appid": api_key,
"units": "metric",
"lang": "es",
}
weather_url = "https://api.openweathermap.org/data/2.5/weather"
response = requests.get(weather_url, params=params)
if response.status_code != 200:
print(json.dumps({"text": "Error al obtener datos del clima"}))
sys.exit(1)
data = response.json()
# ─────────── DATOS CLIMA ─────────── #
temp = f"{round(data['main']['temp'])}°C"
status = data['weather'][0]['description'].capitalize()
status_code = data['weather'][0]['icon']
icon = weather_icons.get(status_code, weather_icons["default"])
temp_feel = f"{round(data['main']['feels_like'])}°C"
temp_feel_text = f"Sensación de {temp_feel}"
temp_min = f"{round(data['main']['temp_min'])}°C"
temp_max = f"{round(data['main']['temp_max'])}°C"
temp_min_max = f" {temp_min}\t\t {temp_max}"
wind_speed = f"{round(data['wind']['speed'] * 3.6)} km/h"
wind_text = f" {wind_speed}"
humidity = f"{data['main']['humidity']}%"
humidity_text = f" {humidity}"
visibility = data.get('visibility', 0)
visibility_km = f"{visibility / 1000} km"
visibility_text = f" {visibility_km}"
# ─────────── CALIDAD DEL AIRE ─────────── #
aqi_url = "https://api.openweathermap.org/data/2.5/air_pollution"
aqi_response = requests.get(aqi_url, params=params)
if aqi_response.status_code == 200:
aqi = aqi_response.json()['list'][0]['main']['aqi']
aqi_levels = {1: "Bueno", 2: "Moderado", 3: "Perjudicial", 4: "Malo", 5: "Muy malo"}
air_quality_index = aqi_levels.get(aqi, "N/A")
else:
air_quality_index = "N/A"
# ─────────── PRONÓSTICO ─────────── #
forecast_url = "https://api.openweathermap.org/data/2.5/forecast"
forecast_response = requests.get(forecast_url, params=params)
if forecast_response.status_code == 200:
forecast_data = forecast_response.json()
precipitation = forecast_data['list'][0].get('pop', 0) * 100
prediction = f"\n\n {precipitation:.0f}% probabilidad de lluvia"
alerta_lluvia = check_rain_alert(forecast_data)
pronostico_dias = get_daily_forecast(forecast_data)
else:
prediction = ""
alerta_lluvia = ""
pronostico_dias = ""
# ─────────── TOOLTIP ─────────── #
time_of_day = "Día" if status_code.endswith('d') else "Noche"
ubicacion = f"{ciudad}, {region}, {pais} ({latitud:.2f}, {longitud:.2f})"
tooltip_text = ""
if alerta_lluvia:
tooltip_text += f"{alerta_lluvia}\n\n"
tooltip_text += str.format(
"{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}\n\n{}",
f'{temp}',
f"{icon}",
f"{status} ({time_of_day})",
f"{temp_feel_text}",
f"{temp_min_max}",
f"{wind_text}\t{humidity_text}",
f"{visibility_text}\tICA {air_quality_index}",
f"{prediction}",
f"Ubicación: {ubicacion}"
)
if pronostico_dias:
tooltip_text += f"\n\nPróximos días:\n{pronostico_dias}"
# ─────────── OUTPUT PARA WAYBAR ─────────── #
out_data = {
"text": f"{icon} {temp}",
"alt": f"{status} ({time_of_day})",
"tooltip": tooltip_text,
"class": status_code,
}
print(json.dumps(out_data))