Port sunset script to Python for maintainability
This commit is contained in:
parent
68b2e276c3
commit
d8764acf00
3 changed files with 89 additions and 66 deletions
88
scripts/sunset.py
Normal file
88
scripts/sunset.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
#!/bin/env python
|
||||
"""Sets the monitor temperature based on the current time using Hyprsunset"""
|
||||
from datetime import datetime
|
||||
from os import environ
|
||||
from socket import AF_UNIX, SOCK_STREAM, socket
|
||||
|
||||
|
||||
def find_hyprsunset_socket() -> str:
|
||||
"""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:
|
||||
"""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`"""
|
||||
return (x - edges[0]) / (edges[1] - edges[0])
|
||||
|
||||
|
||||
def smoothstep(x: float, edges: LerpRange) -> float:
|
||||
"""Smoothly interpolates between the `edges` based on `x`"""
|
||||
# Technically I should bounds check but whatever
|
||||
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:
|
||||
"""Converts (H, M, S) into [0, 1] representing how far through the day it is"""
|
||||
TOTAL_SECONDS = 24 * 60 * 60
|
||||
elapsed = (((hours * 60) + minutes) * 60) + seconds
|
||||
return elapsed / TOTAL_SECONDS
|
||||
|
||||
|
||||
def current_elapsed() -> float:
|
||||
"""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"""
|
||||
apply_temperature(
|
||||
calculate_temperature(
|
||||
current_elapsed(),
|
||||
to_elapsed(sunrise),
|
||||
to_elapsed(sunset),
|
||||
temperature
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main((5, 7), (21, 23), (2500.0, 6000.0))
|
|
@ -1,65 +0,0 @@
|
|||
#!/bin/env sh
|
||||
|
||||
SUNRISE_START="$((5 * 60 * 60))" # When to start turning up temperature
|
||||
SUNRISE_END="$((7 * 60 * 60))" # When to reach max temperature
|
||||
|
||||
SUNSET_START="$((21 * 60 * 60))" # When to start turning down temperature
|
||||
SUNSET_END="$((23 * 60 * 60))" # When to reach min temperature
|
||||
|
||||
DAY_TEMP="6000" # Display temperature (K) to use in full daylight
|
||||
NIGHT_TEMP="2400" # Display temperature (K) to use at night
|
||||
|
||||
CURRENT="$(((($(date '+%-H') * 60) + $(date '+%-M')) * 60 + $(date '+%-S')))" # Time in seconds since start of day
|
||||
|
||||
# Performs a calculation using an argument containing an input string
|
||||
calc() {
|
||||
echo "scale=4; $1" | bc
|
||||
}
|
||||
|
||||
# Evaluates a boolean expression on an argument containing an input string
|
||||
bool() {
|
||||
echo "scale=1; $1" | bc
|
||||
}
|
||||
|
||||
# GLSL Smoothstep, takes a single number as an argument
|
||||
smoothstep() {
|
||||
if [ "$(bool "$1 <= 0")" = "1" ]; then
|
||||
echo 0
|
||||
elif [ "$(bool "$1 >= 1")" = "1" ]; then
|
||||
echo 1
|
||||
else
|
||||
calc "$1 * $1 * (3 - 2 * $1)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Interpolates between the 4th and 5th arguments based on the value of the 2nd in relation to the 1st and 3rd
|
||||
interpolate() {
|
||||
LOWER_IN="$1"
|
||||
VALUE="$2"
|
||||
UPPER_IN="$3"
|
||||
LOWER_OUT="$4"
|
||||
UPPER_OUT="$5"
|
||||
|
||||
if [ "$((VALUE <= LOWER_IN))" = "1" ]; then
|
||||
echo "$LOWER_OUT"
|
||||
elif [ "$((VALUE < UPPER_IN))" = "1" ]; then
|
||||
calc "$LOWER_OUT + (($UPPER_OUT - $LOWER_OUT) * $(smoothstep "$(calc "($VALUE - $LOWER_IN) / ($UPPER_IN - $LOWER_IN)")"))"
|
||||
else
|
||||
echo "$UPPER_OUT"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$((CURRENT <= SUNRISE_START))" = "1" ]; then
|
||||
TEMP="$NIGHT_TEMP"
|
||||
elif [ "$((CURRENT < SUNRISE_END))" = "1" ]; then
|
||||
TEMP="$(interpolate "$SUNRISE_START" "$CURRENT" "$SUNRISE_END" "$NIGHT_TEMP" "$DAY_TEMP")"
|
||||
elif [ "$((CURRENT <= SUNSET_START))" = "1" ]; then
|
||||
TEMP="$DAY_TEMP"
|
||||
elif [ "$((CURRENT < SUNSET_END))" = "1" ]; then
|
||||
TEMP="$(interpolate "$SUNSET_START" "$CURRENT" "$SUNSET_END" "$DAY_TEMP" "$NIGHT_TEMP")"
|
||||
else
|
||||
TEMP="$NIGHT_TEMP"
|
||||
fi
|
||||
|
||||
# TODO: Figure out a nice way to wait until Hyprland has properly started up before running this
|
||||
hyprctl hyprsunset temperature "$TEMP"
|
|
@ -3,4 +3,4 @@ Description=Sets the monitor temperature based on the time of day
|
|||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/home/mbradley/scripts/sunset.sh
|
||||
ExecStart=/home/mbradley/scripts/sunset.py
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue