Simon Bruder
10b8d432d5
This applies the REUSE specification to the repository, so the licensing information can be tracked for every file individually.
89 lines
2.6 KiB
Python
Executable file
89 lines
2.6 KiB
Python
Executable file
#!/usr/bin/env python3
|
||
|
||
# SPDX-FileCopyrightText: 2021 Simon Bruder <simon@sbruder.de>
|
||
#
|
||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||
|
||
from astral.location import Location, LocationInfo
|
||
from math import cos, pi
|
||
from time import sleep
|
||
import datetime
|
||
import os
|
||
import paho.mqtt.client as mqtt
|
||
import sys
|
||
|
||
|
||
def time_as_float_hours(time: datetime.time) -> float:
|
||
return time.hour + time.minute / 60 + time.second / 3600
|
||
|
||
|
||
def set_color(client: mqtt.Client, red: int, green: int, blue: int, retain=True):
|
||
client.reconnect()
|
||
# my wordclock’s red and green LEDs seem to be switched
|
||
client.publish("wordclock/color/red", green, retain=True)
|
||
client.publish("wordclock/color/green", red, retain=True)
|
||
client.publish("wordclock/color/blue", blue, retain=True)
|
||
|
||
|
||
def get_color_for_time(time: datetime.time, base=(60, 60, 60)) -> (int, int, int):
|
||
if time.hour >= 22 or time.hour < 7:
|
||
# night mode: dim red
|
||
return (3, 0, 0)
|
||
else:
|
||
# day mode: calculated depending on sun (https://www.desmos.com/calculator/nrseefnaom)
|
||
now = time_as_float_hours(time)
|
||
|
||
# returns at least (3, 3, 3)
|
||
min_factors = (3 / base[0], 3 / base[1], 3 / base[2])
|
||
|
||
rate = 5
|
||
|
||
location_info = LocationInfo(
|
||
timezone="Europe/Berlin", latitude=49.52, longitude=10.18
|
||
)
|
||
location = Location(location_info)
|
||
sunrise = time_as_float_hours(location.sunrise().time())
|
||
sunset = time_as_float_hours(location.sunset().time())
|
||
|
||
factors = []
|
||
for min_factor in min_factors:
|
||
factor = min_factor
|
||
if now > sunrise and now < sunset:
|
||
factor = 1 - (
|
||
(1 - min_factor) * cos((pi / (sunrise - sunset)) * (now - sunset))
|
||
) ** (2 * rate)
|
||
factors.append(factor)
|
||
|
||
return (
|
||
round(base[0] * factors[0]),
|
||
round(base[1] * factors[1]),
|
||
round(base[2] * factors[2]),
|
||
)
|
||
|
||
|
||
def update(client: mqtt.Client):
|
||
time = datetime.datetime.now().time()
|
||
color = get_color_for_time(time)
|
||
print(f"{time}: setting color to {color}")
|
||
sys.stdout.flush()
|
||
set_color(client, *color)
|
||
pass
|
||
|
||
|
||
client = mqtt.Client("wordclock.py")
|
||
|
||
user = os.environ["WORDCLOCK_MQTT_USER"]
|
||
try:
|
||
password = os.environ["WORDCLOCK_MQTT_PASSWORD"]
|
||
except KeyError:
|
||
with open(os.environ["WORDCLOCK_MQTT_PASSWORD_FILE"]) as f:
|
||
password = f.read().rstrip()
|
||
host = os.environ["WORDCLOCK_MQTT_HOST"]
|
||
|
||
client.username_pw_set(user, password)
|
||
client.connect(host, 1883, 60)
|
||
|
||
while True:
|
||
update(client)
|
||
sleep(300)
|