pkgs: Add wordclock-dimmer (including module)
This commit is contained in:
parent
a02d3cb883
commit
cce86ac2c9
|
@ -34,6 +34,7 @@ in
|
|||
# All modules are imported but non-essential modules are activated by
|
||||
# configuration options
|
||||
imports = [
|
||||
../pkgs/modules.nix
|
||||
./cups.nix
|
||||
./docker.nix
|
||||
./fonts.nix
|
||||
|
|
|
@ -3,4 +3,6 @@ self: super: {
|
|||
|
||||
osu-lazer = super.callPackage ./osu-lazer { inherit (super) osu-lazer; };
|
||||
osu-lazer-container = super.callPackage ./osu-lazer-container { };
|
||||
|
||||
wordclock-dimmer = super.python3Packages.callPackage ./wordclock-dimmer { };
|
||||
}
|
||||
|
|
5
pkgs/modules.nix
Normal file
5
pkgs/modules.nix
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
imports = [
|
||||
./wordclock-dimmer/module.nix
|
||||
];
|
||||
}
|
20
pkgs/wordclock-dimmer/default.nix
Normal file
20
pkgs/wordclock-dimmer/default.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
{ lib, buildPythonApplication, python3Packages }:
|
||||
buildPythonApplication rec {
|
||||
name = "wordclock-dimmer";
|
||||
|
||||
src = ./wordclock-dimmer.py;
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
astral
|
||||
paho-mqtt
|
||||
];
|
||||
|
||||
dontUnpack = true;
|
||||
format = "other";
|
||||
|
||||
installPhase = ''
|
||||
install -D $src $out/bin/wordclock-dimmer
|
||||
'';
|
||||
|
||||
meta.license = lib.licenses.mit;
|
||||
}
|
63
pkgs/wordclock-dimmer/module.nix
Normal file
63
pkgs/wordclock-dimmer/module.nix
Normal file
|
@ -0,0 +1,63 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.services.wordclock-dimmer;
|
||||
in
|
||||
{
|
||||
options.services.wordclock-dimmer = {
|
||||
enable = lib.mkEnableOption "wordclock-dimmer";
|
||||
mqtt = {
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
password = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enable -> (
|
||||
(cfg.mqtt.password != null || cfg.mqtt.passwordFile != null)
|
||||
&& (cfg.mqtt.password == null || cfg.mqtt.passwordFile == null)
|
||||
);
|
||||
message = "One of `services.wordclock-dimmer.mqtt.password` and `services.wordclock-dimmer.mqtt.passwordFile` has to be set.";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.wordclock-dimmer = lib.mkIf cfg.enable {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
environment = with cfg.mqtt; {
|
||||
WORDCLOCK_MQTT_USER = user;
|
||||
WORDCLOCK_MQTT_HOST = host;
|
||||
} // lib.optionalAttrs (password != null) {
|
||||
WORDCLOCK_MQTT_PASSWORD = password;
|
||||
} // lib.optionalAttrs (passwordFile != null) {
|
||||
WORDCLOCK_MQTT_PASSWORD_FILE = passwordFile;
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.wordclock-dimmer}/bin/wordclock-dimmer";
|
||||
Restart = "always";
|
||||
|
||||
# systemd-analyze --no-pager security wordclock-dimmer.service
|
||||
CapabilityBoundingSet = null;
|
||||
DynamicUser = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHome = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
SystemCallFilter = "@system-service";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
82
pkgs/wordclock-dimmer/wordclock-dimmer.py
Executable file
82
pkgs/wordclock-dimmer/wordclock-dimmer.py
Executable file
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python3
|
||||
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):
|
||||
# my wordclock’s red and green LED’s 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)
|
Loading…
Reference in a new issue