Files
nix-config/modules/home/programs/waybar/scripts/weather.nix
2025-07-20 18:35:14 -05:00

440 lines
19 KiB
Nix
Raw Permalink 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.
{ config, lib, pkgs, ... }:
let
cfg = config.mjallen.programs.waybar;
waybar-weather = pkgs.writeScriptBin "waybar-weather" ''
#!/usr/bin/env nix-shell
#! nix-shell -i python3 --pure
#! nix-shell -p python3 python3Packages.requests
"""get waybar weather"""
import os
import json
import shutil
from datetime import datetime, timedelta
import requests
WWO_CODE = {
"113": "Sunny",
"116": "PartlyCloudy",
"119": "Cloudy",
"122": "VeryCloudy",
"143": "Fog",
"176": "LightShowers",
"179": "LightSleetShowers",
"182": "LightSleet",
"185": "LightSleet",
"200": "ThunderyShowers",
"227": "LightSnow",
"230": "HeavySnow",
"248": "Fog",
"260": "Fog",
"263": "LightShowers",
"266": "LightRain",
"281": "LightSleet",
"284": "LightSleet",
"293": "LightRain",
"296": "LightRain",
"299": "HeavyShowers",
"302": "HeavyRain",
"305": "HeavyShowers",
"308": "HeavyRain",
"311": "LightSleet",
"314": "LightSleet",
"317": "LightSleet",
"320": "LightSnow",
"323": "LightSnowShowers",
"326": "LightSnowShowers",
"329": "HeavySnow",
"332": "HeavySnow",
"335": "HeavySnowShowers",
"338": "HeavySnow",
"350": "LightSleet",
"353": "LightShowers",
"356": "HeavyShowers",
"359": "HeavyRain",
"362": "LightSleetShowers",
"365": "LightSleetShowers",
"368": "LightSnowShowers",
"371": "HeavySnowShowers",
"374": "LightSleetShowers",
"377": "LightSleet",
"386": "ThunderyShowers",
"389": "ThunderyHeavyRain",
"392": "ThunderySnowShowers",
"395": "HeavySnowShowers",
}
WEATHER_SYMBOL = {
"Unknown": "",
"Cloudy": "",
"Fog": "",
"HeavyRain": "",
"HeavyShowers": "",
"HeavySnow": "",
"HeavySnowShowers": "",
"LightRain": "",
"LightShowers": "",
"LightSleet": "",
"LightSleetShowers": "",
"LightSnow": "",
"LightSnowShowers": "",
"PartlyCloudy": "󰖕",
"Sunny": "",
"ThunderyHeavyRain": "",
"ThunderyShowers": "",
"ThunderySnowShowers": "",
"VeryCloudy": "",
}
WEATHER_CODES = {key: WEATHER_SYMBOL[value] for key, value in WWO_CODE.items()}
WIND_DIRECTION = {
"S": "",
"SW": "",
"W": "",
"NW": "",
"N": "",
"NE": "",
"E": "",
"SE": "",
}
MOON_PHASES = (
"󰽤", "󰽧", "󰽡", "󰽨", "󰽢", "󰽦", "󰽣", "󰽥"
)
WEATHER_SYMBOL_WI_DAY = {
"Unknown": "",
"Cloudy": "",
"Fog": "",
"HeavyRain": "",
"HeavyShowers": "",
"HeavySnow": "",
"HeavySnowShowers": "",
"LightRain": "",
"LightShowers": "",
"LightSleet": "",
"LightSleetShowers": "",
"LightSnow": "",
"LightSnowShowers": "",
"PartlyCloudy": "󰖕",
"Sunny": "",
"ThunderyHeavyRain": "",
"ThunderyShowers": "",
"ThunderySnowShowers": "",
"VeryCloudy": "",
}
WEATHER_CODES_WI_DAY = {key: WEATHER_SYMBOL_WI_DAY[value] for key, value in WWO_CODE.items()}
WEATHER_SYMBOL_WI_NIGHT = {
"Unknown": "",
"Cloudy": "",
"Fog": "",
"HeavyRain": "",
"HeavyShowers": "",
"HeavySnow": "",
"HeavySnowShowers": "",
"LightRain": "",
"LightShowers": "",
"LightSleet": "",
"LightSleetShowers": "",
"LightSnow": "",
"LightSnowShowers": "",
"PartlyCloudy": "󰼱",
"Sunny": "󰖔",
"ThunderyHeavyRain": "",
"ThunderyShowers": "",
"ThunderySnowShowers": "",
"VeryCloudy": "",
}
WEATHER_CODES_WI_NIGHT = {key: WEATHER_SYMBOL_WI_NIGHT[value] for key, value in WWO_CODE.items()}
WEATHER_SYMBOL_WEGO = {
"Unknown": [
" .-. ",
" __) ",
" ( ",
" `- ",
" "],
"Sunny": [
'<span foreground=\"#FFFF00\"> \\ / </span>',
'<span foreground=\"#FFFF00\"> .-. </span>',
'<span foreground=\"#FFFF00\"> ( ) </span>',
'<span foreground=\"#FFFF00\"> `- </span>',
'<span foreground=\"#FFFF00\"> / \\ </span>'],
"PartlyCloudy": [
'<span foreground=\"#FFFF00\"> \\ / </span>',
'<span foreground=\"#FFFF00\"> _ /\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> \\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
' '
],
"Cloudy": [
' ',
'<span foreground=\"#BBBBBB\"> .--. </span>',
'<span foreground=\"#BBBBBB\"> .-( ). </span>',
'<span foreground=\"#BBBBBB\"> (___.__)__) </span>',
' '],
"VeryCloudy": [
' ',
'<span foreground=\"#585858\"; font-weight: bold;"> .--. </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> .-( ). </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> (___.__)__) </span>',
' '],
"LightShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
'<span foreground=\"#87afff\";"> </span>',
'<span foreground=\"#87afff\";"> </span>'],
"HeavyShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#585858\"; font-weight: bold;">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#585858\"; font-weight: bold;">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#585858\"; font-weight: bold;">(___(__) </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>'],
"LightSnowShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
'<span foreground=\"#eeeeee\";"> * * * </span>',
'<span foreground=\"#eeeeee\";"> * * * </span>'],
"HeavySnowShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#585858\"; font-weight: bold;">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#585858\"; font-weight: bold;">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#585858\"; font-weight: bold;">(___(__) </span>',
'<span foreground=\"#eeeeee\"; font-weight: bold;"> * * * * </span>',
'<span foreground=\"#eeeeee\"; font-weight: bold;"> * * * * </span>'],
"LightSleetShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
'<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">*</span>"<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">* </span>',
'<span foreground=\"#eeeeee\";"> *</span>"<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">*</span>"<span foreground=\"#87afff\";"> </span>'],
"ThunderyShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
'<span foreground=\"#ffff87\";"> \\</span>"<span foreground=\"#87afff\";"> </span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#87afff\";"> </span>',
'<span foreground=\"#87afff\";"> </span>'],
"ThunderyHeavyRain": [
'<span foreground=\"#585858\"; font-weight: bold;"> .-. </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> ( ). </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> (___(__) </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#0000ff\";"></span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#0000ff\";"> </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#0000ff\";"> </span>'],
"ThunderySnowShowers": [
'<span foreground=\"#FFFF00\"> _`/\'\'</span>"<span foreground=\"#BBBBBB\">.-. </span>',
'<span foreground=\"#FFFF00\"> ,\\_</span>"<span foreground=\"#BBBBBB\">( ). </span>',
'<span foreground=\"#FFFF00\"> /</span>"<span foreground=\"#BBBBBB\">(___(__) </span>',
'<span foreground=\"#eeeeee\";"> *</span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#eeeeee\";">*</span>"<span foreground=\"#ffff87\";">\\</span>"<span foreground=\"#eeeeee\";">* </span>',
'<span foreground=\"#eeeeee\";"> * * * </span>'],
"LightRain": [
'<span foreground=\"#BBBBBB\"> .-. </span>',
'<span foreground=\"#BBBBBB\"> ( ). </span>',
'<span foreground=\"#BBBBBB\"> (___(__) </span>',
'<span foreground=\"#87afff\";"> </span>',
'<span foreground=\"#87afff\";"> </span>'],
"HeavyRain": [
'<span foreground=\"#585858\"; font-weight: bold;"> .-. </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> ( ). </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> (___(__) </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>',
'<span foreground=\"#0000ff\"; font-weight: bold;"> </span>'],
"LightSnow": [
'<span foreground=\"#BBBBBB\"> .-. </span>',
'<span foreground=\"#BBBBBB\"> ( ). </span>',
'<span foreground=\"#BBBBBB\"> (___(__) </span>',
'<span foreground=\"#eeeeee\";"> * * * </span>',
'<span foreground=\"#eeeeee\";"> * * * </span>'],
"HeavySnow": [
'<span foreground=\"#585858\"; font-weight: bold;"> .-. </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> ( ). </span>',
'<span foreground=\"#585858\"; font-weight: bold;"> (___(__) </span>',
'<span foreground=\"#eeeeee\"; font-weight: bold;"> * * * * </span>',
'<span foreground=\"#eeeeee\"; font-weight: bold;"> * * * * </span>'],
"LightSleet": [
'<span foreground=\"#BBBBBB\"> .-. </span>',
'<span foreground=\"#BBBBBB\"> ( ). </span>',
'<span foreground=\"#BBBBBB\"> (___(__) </span>',
'<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">*</span>"<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">* </span>',
'<span foreground=\"#eeeeee\";"> *</span>"<span foreground=\"#87afff\";"> </span>"<span foreground=\"#eeeeee\";">*</span>"<span foreground=\"#87afff\";"> </span>'],
"Fog": [
' ',
'<span foreground=\"#c0c0c0\";"> _ - _ - _ - </span>',
'<span foreground=\"#c0c0c0\";"> _ - _ - _ </span>',
'<span foreground=\"#c0c0c0\";"> _ - _ - _ - </span>',
' '],
}
WEATHER_CODES_WEGO = {key: WEATHER_SYMBOL_WEGO[value] for key, value in WWO_CODE.items()}
data = {}
def format_time(time):
"""get the time formatted"""
return datetime.strptime(format_24_time(time), "%H").strftime("%I %p")
def format_24_time(time):
"""get the time formatted"""
return time.replace("00", "").zfill(2)
def format_temp(temp):
"""get the temp formatted"""
return (temp + "°").ljust(3)
def format_chances(hour):
"""get the chances formatted"""
chances = {
"chanceoffog": "Fog",
"chanceoffrost": "Frost",
"chanceofovercast": "Overcast",
"chanceofrain": "Rain",
"chanceofsnow": "Snow",
"chanceofsunshine": "Sunshine",
"chanceofthunder": "Thunder",
"chanceofwindy": "Wind",
}
conditions = []
for chance, event in chances.items():
if int(hour[chance]) > 0:
conditions.append(event + " " + hour[chance] + "%")
return ", ".join(conditions)
def build_text(current_condition):
"""build the text string"""
feels_like_f = current_condition["FeelsLikeF"]
weather_code = current_condition["weatherCode"]
tempint = int(feels_like_f)
extrachar = ""
if 0 < tempint < 10:
extrachar = "+"
current_weather = f"{WEATHER_CODES[weather_code]} {extrachar} {feels_like_f}°F"
return current_weather
def build_tooltip(current_condition, astronomy, moon_icon):
"""build the tooltip text"""
weather_description = current_condition['weatherDesc'][0]['value']
feels_like_f = current_condition["FeelsLikeF"]
temp_f = current_condition['temp_F']
humidity = current_condition['humidity']
wind_speed = current_condition['windspeedMiles']
wind_dir = current_condition['winddir16Point']
moon_phase = astronomy['moon_phase']
wego = WEATHER_CODES_WEGO[current_condition['weatherCode']]
tooltip = f"{wego[0]}{weather_description} {temp_f}°\n"
tooltip += f"{wego[1]}Feels like: {feels_like_f}°\n"
tooltip += f"{wego[2]}Wind: {wind_speed}mph {WIND_DIRECTION[wind_dir]}\n"
tooltip += f"{wego[3]}Humidity: {humidity}%\n"
tooltip += f"{wego[4]}Moon phase: {moon_phase} " + moon_icon + "\n"
return tooltip
def build_forecast(weather):
"""build a 3 day forecast"""
tooltip = "\n"
for i, day in enumerate(weather):
# determine day
if i == 0:
tooltip += "Today, "
if i == 1:
tooltip += "Tomorrow, "
# format the date
date = datetime.strptime(day['date'], "%Y-%m-%d").strftime("%a %b %d %Y")
tooltip += f"<b>{date}</b>\n"
# set the high and low
max_temp = day['maxtempF']
min_temp = day['mintempF']
tooltip += f" {max_temp}°F {min_temp}°F"
sunrise = day['astronomy'][0]['sunrise']
sunset = day['astronomy'][0]['sunset']
tooltip += f" {sunrise} {sunset}\n"
tooltip += build_hourly_forecast(i, day['hourly'], sunrise, sunset)
return tooltip
def build_hourly_forecast(day_num, hourly, sunrise, sunset):
"""build an hourly forecast"""
sunrise_hour = datetime.strptime(sunrise, "%I:%M %p").hour
sunset_hour = datetime.strptime(sunset, "%I:%M %p").hour
current_hour = datetime.now().hour
tooltip = ""
for hour in hourly:
time_24_hr = int(format_24_time(hour["time"]))
if day_num == 0:
if time_24_hr < current_hour - 2:
continue
# determine which code to use
if is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
codes = WEATHER_CODES_WI_NIGHT
else:
codes = WEATHER_CODES_WI_DAY
current_time = format_time(hour['time'])
current_weather_code = codes[hour['weatherCode']]
feels_like = format_temp(hour['FeelsLikeF'])
weather_desc = hour['weatherDesc'][0]['value']
current_chances = format_chances(hour)
tooltip += f"{current_time} {current_weather_code} "
tooltip += f"{feels_like} {weather_desc}, {current_chances}\n"
return tooltip
def is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
"""returns true if the hour is night"""
before_sunrise = time_24_hr < sunrise_hour
after_sunset = time_24_hr > sunset_hour
return after_sunset or before_sunrise
def get_wttr_json():
"""get the weather json"""
weather = requests.get("https://wttr.in/?u&format=j1", timeout=30).json()
moon = requests.get("https://wttr.in/?format=%m", timeout=30)
moon_icon = moon.text
current_condition = weather["current_condition"][0]
astronomy = weather["weather"][0]['astronomy'][0]
data["text"] = build_text(current_condition)
data["tooltip"] = build_tooltip(current_condition, astronomy, moon_icon)
data["tooltip"] += build_forecast(weather["weather"])
return json.dumps(data)
def main():
"""main"""
try:
print(get_wttr_json())
except Exception as e:
print(e)
main()
'';
in
{
imports = [ ../options.nix ];
config = lib.mkIf cfg.enable {
home.packages = [ waybar-weather ];
};
}