Optimize Python sunset script
This commit is contained in:
parent
d8764acf00
commit
08ecda72ee
2 changed files with 30 additions and 63 deletions
91
scripts/sunset.py
Normal file → Executable file
91
scripts/sunset.py
Normal file → Executable file
|
@ -5,84 +5,51 @@ from os import environ
|
||||||
from socket import AF_UNIX, SOCK_STREAM, socket
|
from socket import AF_UNIX, SOCK_STREAM, socket
|
||||||
|
|
||||||
|
|
||||||
def find_hyprsunset_socket() -> str:
|
type Range = tuple[float, float]
|
||||||
"""Gets the socket file location"""
|
|
||||||
xdg_runtime_directory = environ["XDG_RUNTIME_DIR"]
|
|
||||||
hyprland_instance_signature = environ["HYPRLAND_INSTANCE_SIGNATURE"]
|
|
||||||
return f"{xdg_runtime_directory}/hypr/{hyprland_instance_signature}/.hyprsunset.sock"
|
|
||||||
|
|
||||||
|
|
||||||
def apply_temperature(temperature: float) -> None:
|
def lerped_amount(x: float, edges: Range) -> float:
|
||||||
"""Uses IPC to tell Hyprsunset to update the monitor temperature"""
|
|
||||||
hyprsunset = socket(AF_UNIX, SOCK_STREAM)
|
|
||||||
hyprsunset.connect(find_hyprsunset_socket())
|
|
||||||
message = f"temperature {temperature:.0f}".encode()
|
|
||||||
sent = 0
|
|
||||||
while sent < len(message):
|
|
||||||
sent += hyprsunset.send(message[sent:])
|
|
||||||
|
|
||||||
|
|
||||||
type Range[T] = tuple[T, T]
|
|
||||||
type LerpRange = Range[float]
|
|
||||||
type HourRange = Range[int]
|
|
||||||
type ElapsedRange = Range[float]
|
|
||||||
type TemperatureRange = Range[float]
|
|
||||||
|
|
||||||
|
|
||||||
def lerped_amount(x: float, edges: LerpRange) -> float:
|
|
||||||
"""How far `x` is between `values`"""
|
"""How far `x` is between `values`"""
|
||||||
return (x - edges[0]) / (edges[1] - edges[0])
|
return (x - edges[0]) / (edges[1] - edges[0])
|
||||||
|
|
||||||
|
|
||||||
def smoothstep(x: float, edges: LerpRange) -> float:
|
def smoothstep(x: float, edges: Range) -> float:
|
||||||
"""Smoothly interpolates between the `edges` based on `x`"""
|
"""Smoothly interpolates between the `edges` based on `x`"""
|
||||||
# Technically I should bounds check but whatever
|
# Technically I should bounds check but whatever
|
||||||
return (x * x * (3.0 - 2.0 * x)) * (edges[1] - edges[0]) + edges[0]
|
return (x * x * (3.0 - 2.0 * x)) * (edges[1] - edges[0]) + edges[0]
|
||||||
|
|
||||||
|
|
||||||
def calculate_temperature(elapsed: float, sunrise: ElapsedRange, sunset: ElapsedRange, temperature: TemperatureRange) -> float:
|
|
||||||
"""Determines which temperature to set the monitors to"""
|
|
||||||
if elapsed <= sunrise[0]:
|
|
||||||
return temperature[0]
|
|
||||||
elif elapsed < sunrise[1]:
|
|
||||||
return smoothstep(lerped_amount(elapsed, sunrise), temperature)
|
|
||||||
elif elapsed <= sunset[0]:
|
|
||||||
return temperature[1]
|
|
||||||
elif elapsed < sunset[1]:
|
|
||||||
return smoothstep(lerped_amount(elapsed, sunrise), temperature[::-1])
|
|
||||||
else:
|
|
||||||
return temperature[0]
|
|
||||||
|
|
||||||
|
|
||||||
def day_elapsed(hours: int = 0, minutes: int = 0, seconds: int = 0) -> float:
|
def day_elapsed(hours: int = 0, minutes: int = 0, seconds: int = 0) -> float:
|
||||||
"""Converts (H, M, S) into [0, 1] representing how far through the day it is"""
|
"""Converts (H, M, S) into [0, 1] representing how far through the day it is"""
|
||||||
TOTAL_SECONDS = 24 * 60 * 60
|
return ((((hours * 60) + minutes) * 60) + seconds) / 86400
|
||||||
elapsed = (((hours * 60) + minutes) * 60) + seconds
|
|
||||||
return elapsed / TOTAL_SECONDS
|
|
||||||
|
|
||||||
|
|
||||||
def current_elapsed() -> float:
|
def main(sunrise: Range, sunset: Range, temp_range: Range) -> None:
|
||||||
"""Time through the day represented in [0, 1]"""
|
|
||||||
current = datetime.now()
|
|
||||||
return day_elapsed(current.hour, current.minute, current.second)
|
|
||||||
|
|
||||||
|
|
||||||
def to_elapsed(hours: HourRange) -> ElapsedRange:
|
|
||||||
"""Converts a range of hours into a range representing how far through the day is is"""
|
|
||||||
return day_elapsed(hours[0]), day_elapsed(hours[1])
|
|
||||||
|
|
||||||
|
|
||||||
def main(sunrise: HourRange, sunset: HourRange, temperature: TemperatureRange) -> None:
|
|
||||||
"""Adjusts the monitor temperature based on the current time"""
|
"""Adjusts the monitor temperature based on the current time"""
|
||||||
apply_temperature(
|
now = datetime.now()
|
||||||
calculate_temperature(
|
elapsed = day_elapsed(now.hour, now.minute, now.second)
|
||||||
current_elapsed(),
|
|
||||||
to_elapsed(sunrise),
|
if elapsed <= sunrise[0]:
|
||||||
to_elapsed(sunset),
|
temperature = temp_range[0]
|
||||||
temperature
|
elif elapsed < sunrise[1]:
|
||||||
)
|
temperature = smoothstep(lerped_amount(elapsed, sunrise), temp_range)
|
||||||
)
|
elif elapsed <= sunset[0]:
|
||||||
|
temperature = temp_range[1]
|
||||||
|
elif elapsed < sunset[1]:
|
||||||
|
temperature = smoothstep(lerped_amount(elapsed, sunset), (temp_range[1], temp_range[0]))
|
||||||
|
else:
|
||||||
|
temperature = temp_range[0]
|
||||||
|
|
||||||
|
hyprsunset = socket(AF_UNIX, SOCK_STREAM)
|
||||||
|
# In theory I should use $XDG_RUNTIME_DIR, but for me it's always `/run/user/1000/`
|
||||||
|
hyprsunset.connect(f"/run/user/1000/hypr/{environ["HYPRLAND_INSTANCE_SIGNATURE"]}/.hyprsunset.sock")
|
||||||
|
# In theory the message might not send in one go, but in practice it does do I won't bother handling it
|
||||||
|
_ = hyprsunset.send(f"temperature {temperature:.1f}".encode())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main((5, 7), (21, 23), (2500.0, 6000.0))
|
main(
|
||||||
|
(day_elapsed(5), day_elapsed(7)),
|
||||||
|
(day_elapsed(21), day_elapsed(23)),
|
||||||
|
(2500.0, 6000.0)
|
||||||
|
)
|
||||||
|
|
|
@ -3,4 +3,4 @@ Description=Sets the monitor temperature based on the time of day
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/home/mbradley/scripts/sunset.py
|
ExecStart=/bin/env python -O /home/mbradley/scripts/sunset.py
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue