2025-05-28 18:27:10 +02:00

230 lines
8.5 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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<small>{}</small>",
f'<span size="xx-large">{temp}</span>',
f"<big>{icon}</big>",
f"<big>{status} ({time_of_day})</big>",
f"<small>{temp_feel_text}</small>",
f"<big>{temp_min_max}</big>",
f"{wind_text}\t{humidity_text}",
f"{visibility_text}\tICA {air_quality_index}",
f"<i>{prediction}</i>",
f"Ubicación: {ubicacion}"
)
if pronostico_dias:
tooltip_text += f"\n\n<b>Próximos días:</b>\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))