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
|
# All modules are imported but non-essential modules are activated by
|
||||||
# configuration options
|
# configuration options
|
||||||
imports = [
|
imports = [
|
||||||
|
../pkgs/modules.nix
|
||||||
./cups.nix
|
./cups.nix
|
||||||
./docker.nix
|
./docker.nix
|
||||||
./fonts.nix
|
./fonts.nix
|
||||||
|
|
|
@ -3,4 +3,6 @@ self: super: {
|
||||||
|
|
||||||
osu-lazer = super.callPackage ./osu-lazer { inherit (super) osu-lazer; };
|
osu-lazer = super.callPackage ./osu-lazer { inherit (super) osu-lazer; };
|
||||||
osu-lazer-container = super.callPackage ./osu-lazer-container { };
|
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