Estimate sunup/sundown times for earlier wallpaper changes in winter, update for Qtile 0.34.x

This commit is contained in:
Kevin Alberts 2026-01-06 09:41:51 +01:00
parent 27b41f975d
commit fa5bbee56e
3 changed files with 161 additions and 9 deletions

View file

@ -80,6 +80,11 @@ class Config(BaseConfig):
{'title': 'Transaction splits', 'wm_class': 'homebank'},
]
# Location
loc_latitude = 52.357603
loc_longitude = 6.663761
loc_timezone = "Europe/Amsterdam"
# Autostart applications
apps_autostart_group = [
{'group': "", 'command': ["firefox"]},

View file

@ -5,11 +5,11 @@ import time
import datetime
import socket
import subprocess
from dateutil import tz
from typing import Optional
from libqtile.backend.base import Window
from libqtile.backend.wayland.layer import LayerStatic
from libqtile.backend.wayland.xwindow import XWindow as WaylandXWindow, XStatic as WaylandXStatic
from libqtile.backend.wayland.window import Window as WaylandWindow, Static as WaylandStatic
from libqtile.backend.x11.window import XWindow as XorgXWindow
# Initialize logging
from libqtile.log_utils import logger
@ -30,6 +30,7 @@ logger.warning("Importing kuro utils...")
import kuro.utils.widgets
from kuro.utils import general as utils
from kuro.utils.suntime import Sun
logger.warning("Importing variables and other utils...")
@ -643,11 +644,9 @@ class Kuro(BaseTheme):
try:
w_pid = client.get_pid()
except AttributeError: # Some windows might not have this .get_pid method. Try other ways
if isinstance(client, WaylandXWindow) or isinstance(client, WaylandXStatic):
w_pid = client.surface.pid
elif isinstance(client, XorgXWindow):
if isinstance(client, XorgXWindow):
w_pid = client.get_net_wm_pid()
elif isinstance(client, LayerStatic):
elif isinstance(client, WaylandStatic) or isinstance(client, WaylandInternal):
pass # Wayland background layer 'window'
else:
logger.error(f"Unknown window type {client.__class__.__name__}")
@ -714,11 +713,18 @@ class Kuro(BaseTheme):
wallpapers = []
wallpaper_dir = Config.get("desktop_bg_folder", "")
# Use a wallpaper from the night folder after 9PM and before 6AM
# Get current and sunset/sunrise times using util
sun = Sun(Config.get("loc_latitude", 52.357603), Config.get("loc_longitude", 6.663761))
tzone = tz.gettz(Config.get("loc_timezone", "Europe/Amsterdam"))
cur_time = datetime.datetime.now(tz=tzone)
sunset = sun.get_sunset_time(time_zone=tzone)
sunrise = sun.get_sunrise_time(time_zone=tzone)
logger.warning("Using current time {}, sunset {}, sunrise {} to determine wallpaper.".format(cur_time, sunset, sunrise))
# Use a wallpaper from the night folder after sunset and before sunrise
wallpaper_night_dir = Config.get("desktop_bg_night_folder", "")
if wallpaper_night_dir and os.path.isdir(wallpaper_night_dir):
cur_time = datetime.datetime.now()
if cur_time.hour > 21 or cur_time.hour < 6:
if cur_time > sunset or cur_time < sunrise:
wallpaper_dir = wallpaper_night_dir
try:

141
kuro/utils/suntime.py Normal file
View file

@ -0,0 +1,141 @@
import math
import warnings
from datetime import datetime, timedelta, time, timezone
# Copied from: https://github.com/SatAgro/suntime/blob/master/suntime/suntime.py
# CONSTANT
TO_RAD = math.pi/180.0
class SunTimeException(Exception):
def __init__(self, message):
super(SunTimeException, self).__init__(message)
class Sun:
"""
Approximated calculation of sunrise and sunset datetimes. Adapted from:
https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql
"""
def __init__(self, lat, lon):
self._lat = lat
self._lon = lon
self.lngHour = self._lon / 15
def get_sunrise_time(self, at_date=datetime.now(), time_zone=timezone.utc):
"""
:param at_date: Reference date. datetime.now() if not provided.
:param time_zone: pytz object with .tzinfo() or None
:return: sunrise datetime.
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
"""
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=True)
if time_delta is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
def get_sunset_time(self, at_date=datetime.now(), time_zone=timezone.utc):
"""
Calculate the sunset time for given date.
:param at_date: Reference date. datetime.now() if not provided.
:param time_zone: pytz object with .tzinfo() or None
:return: sunset datetime.
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
"""
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=False)
if time_delta is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
def get_sun_timedelta(self, at_date, time_zone, is_rise_time=True, zenith=90.8):
"""
Calculate sunrise or sunset date.
:param at_date: Reference date
:param time_zone: pytz object with .tzinfo() or None
:param is_rise_time: True if you want to calculate sunrise time.
:param zenith: Sun reference zenith
:return: timedelta showing hour, minute, and second of sunrise or sunset
"""
# If not set get local timezone from datetime
if time_zone is None:
time_zone = datetime.now().tzinfo
# 1. first get the day of the year
N = at_date.timetuple().tm_yday
# 2. convert the longitude to hour value and calculate an approximate time
if is_rise_time:
t = N + ((6 - self.lngHour) / 24)
else: # sunset
t = N + ((18 - self.lngHour) / 24)
# 3a. calculate the Sun's mean anomaly
M = (0.9856 * t) - 3.289
# 3b. calculate the Sun's true longitude
L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
L = self._force_range(L, 360) # NOTE: L adjusted into the range [0,360)
# 4a. calculate the Sun's declination
sinDec = 0.39782 * math.sin(TO_RAD*L)
cosDec = math.cos(math.asin(sinDec))
# 4b. calculate the Sun's local hour angle
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*self._lat))) / (cosDec * math.cos(TO_RAD*self._lat))
if cosH > 1:
return None # The sun never rises on this location (on the specified date)
if cosH < -1:
return None # The sun never sets on this location (on the specified date)
# 4c. finish calculating H and convert into hours
if is_rise_time:
H = 360 - (1/TO_RAD) * math.acos(cosH)
else: # setting
H = (1/TO_RAD) * math.acos(cosH)
H = H / 15
# 5a. calculate the Sun's right ascension
RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
RA = self._force_range(RA, 360) # NOTE: RA adjusted into the range [0,360)
# 5b. right ascension value needs to be in the same quadrant as L
Lquadrant = (math.floor(L/90)) * 90
RAquadrant = (math.floor(RA/90)) * 90
RA = RA + (Lquadrant - RAquadrant)
# 5c. right ascension value needs to be converted into hours
RA = RA / 15
# 6. calculate local mean time of rising/setting
T = H + RA - (0.06571 * t) - 6.622
# 7a. adjust back to UTC
UT = T - self.lngHour
if time_zone:
# 7b. adjust back to local time
UT += time_zone.utcoffset(at_date).total_seconds() / 3600
# 7c. rounding and impose range bounds
UT = round(UT, 2)
# if is_rise_time:
UT = self._force_range(UT, 24)
# 8. return timedelta
return timedelta(hours=UT)
@staticmethod
def _force_range(v, max):
# force v to be >= 0 and < max
if v < 0:
return v + max
elif v >= max:
return v - max
return v