Skip to content

Instantly share code, notes, and snippets.

@Michcioperz
Created March 21, 2021 21:46
Show Gist options
  • Save Michcioperz/5d1cd5e94e516b3cfa347f2317d8f931 to your computer and use it in GitHub Desktop.
Save Michcioperz/5d1cd5e94e516b3cfa347f2317d8f931 to your computer and use it in GitHub Desktop.
dynamic background chooser (ve means violet evergarden)
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ps.astral ps.pytz])"
import dataclasses
import datetime
import enum
import functools
import itertools
import math
import subprocess
import time
import astral
import astral.sun
import pytz
class Phase(enum.Enum):
DAWN = "dawn"
SUNRISE = "sunrise"
SUNSET = "sunset"
DUSK = "dusk"
@dataclasses.dataclass
class Vampire:
timezone: str = "Europe/Warsaw"
city: astral.LocationInfo = astral.LocationInfo(
"Lublin", "Poland", timezone, 51.24, 22.57
)
def sun(self, day: datetime.date) -> astral.sun.sun:
return astral.sun.sun(self.city.observer, date=day, tzinfo=self.city.timezone)
@functools.cached_property
def now(self) -> datetime.datetime:
return pytz.timezone(self.timezone).localize(datetime.datetime.now())
@property
def today(self) -> datetime.date:
return self.now.date()
@property
def yesterday(self) -> datetime.date:
return self.today - datetime.timedelta(days=1)
@property
def tomorrow(self) -> datetime.date:
return self.today + datetime.timedelta(days=1)
def the_last(
self, dt1: datetime.datetime, dt2: datetime.datetime
) -> datetime.datetime:
later, earlier = (dt1, dt2) if dt1 > dt2 else (dt2, dt1)
assert earlier < self.now
return later if later < self.now else earlier
def the_next(
self, dt1: datetime.datetime, dt2: datetime.datetime
) -> datetime.datetime:
later, earlier = (dt1, dt2) if dt1 > dt2 else (dt2, dt1)
assert later > self.now
return earlier if earlier > self.now else later
def next_suns(
self, key: ["dawn", "sunrise", "sunset", "dusk"]
) -> datetime.datetime:
return self.the_next(self.sun(self.today)[key], self.sun(self.tomorrow)[key])
def last_suns(
self, key: ["dawn", "sunrise", "sunset", "dusk"]
) -> datetime.datetime:
return self.the_last(self.sun(self.today)[key], self.sun(self.yesterday)[key])
def phase(self) -> (Phase, Phase):
phases = [Phase.DAWN, Phase.SUNRISE, Phase.SUNSET, Phase.DUSK]
right = sorted((self.next_suns(phase.value), phase) for phase in phases)[0][1]
left = phases[(phases.index(right) - 1) % len(phases)]
return (left, right)
def progress(self) -> float:
left, right = self.phase()
left_dt, right_dt = self.last_suns(left.value), self.next_suns(right.value)
return (self.now - left_dt) / (right_dt - left_dt)
@dataclasses.dataclass
class Criterium:
phases: (Phase, Phase)
path_fstring: str
indices: [int]
def try_match(self, lr: (Phase, Phase)) -> bool:
return self.phases == lr
def choose(self, progress: float) -> str:
return self.path_fstring.format(
self.indices[math.floor(progress * len(self.indices))]
)
criteria = [
Criterium(
phases=(Phase.DAWN, Phase.SUNRISE),
path_fstring="/home/michcioperz/ve-lapses/ep01-sunrise-{:02d}.jpg",
indices=range(1, 87 + 1),
),
Criterium(
phases=(Phase.SUNRISE, Phase.SUNSET),
path_fstring="/home/michcioperz/ve-lapses/ep01-day-{:02d}.jpg",
indices=range(1, 70 + 1),
),
Criterium(
phases=(Phase.SUNSET, Phase.DUSK),
path_fstring="/home/michcioperz/ve-lapses/ep01-day-{:02d}.jpg",
indices=range(71, 97 + 1),
),
Criterium(
phases=(Phase.DUSK, Phase.DAWN),
path_fstring="/home/michcioperz/ve-lapses/ep01-nightskyspinny-{:02d}.jpg",
indices=range(1, 75 + 1),
),
]
def choose() -> str:
v = Vampire()
phase = v.phase()
progress = v.progress()
for crit in criteria:
if crit.try_match(phase):
return crit.choose(progress)
assert False
def daemon():
last = None
while True:
pick = choose()
if last != pick:
last = pick
print(datetime.datetime.now(), pick)
subprocess.check_call(["feh", "--bg-scale", pick])
time.sleep(60)
if __name__ == "__main__":
daemon()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment