Initial scaffold: NixOS + SvelteKit dashboard + Python renderer

This commit is contained in:
Ruben Hensen
2026-06-07 18:15:06 +02:00
commit 50256fa384
25 changed files with 4491 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
{ config, lib, pkgs, self, ... }:
{
imports = [
./hardware.nix
../../modules/eink-dashboard.nix
../../modules/eink-renderer.nix
];
networking.hostName = "dayplanner";
networking.domain = "local";
# WiFi or wired. Default to DHCP — set static IP via your router if you want predictability.
networking.networkmanager.enable = true;
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
time.timeZone = "Europe/Amsterdam";
users.users.ruben = {
isNormalUser = true;
extraGroups = [ "wheel" "gpio" "spi" "dialout" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEiY+6AEuKQHyt4Mpg/IbyoIkhg76u3xFPdXGpTsmwxT ruben.hensen@protonmail.com"
];
};
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.PermitRootLogin = "no";
};
# Pull-deploy: every commit on `main` lands on the Pi within ~60 s.
# The remote URL must be reachable from the Pi (push to GitHub/Gitea).
services.comin = {
enable = true;
remotes = [{
name = "origin";
url = "https://git.rubenhensen.nl/ruben/eink.git";
branches.main.name = "main";
}];
hostname = "dayplanner";
};
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 14d";
};
environment.systemPackages = with pkgs; [
git
htop
vim
];
eink.dashboard.enable = true;
eink.dashboard.caldavUser = "ruben";
eink.renderer.enable = true;
eink.renderer.dashboardUrl = "http://127.0.0.1:3000";
system.stateVersion = "25.11";
}
+54
View File
@@ -0,0 +1,54 @@
{ config, lib, pkgs, modulesPath, ... }:
{
# Pi 4 aarch64. Boot from the official NixOS sd-image; we don't generate
# the image from this flake — flash the upstream aarch64 sd-image once,
# then `nixos-rebuild switch --flake .#dayplanner --target-host` swaps
# the running system over and comin takes over from there.
imports = [
"${modulesPath}/installer/sd-card/sd-image-aarch64.nix"
];
# Drop the default channel; we deploy from the flake.
sdImage.compressImage = false;
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true;
# The Waveshare 12.48" HAT is driven entirely from userspace Python via
# /dev/spidev*. We only need SPI enabled in the device tree and the
# gpio/spi groups available to the renderer user.
boot.kernelParams = [ "console=serial0,115200" "console=tty1" ];
# Enable SPI0 (CE0/CE1) and the extra GPIOs the HAT needs.
# See: https://www.waveshare.com/wiki/12.48inch_e-Paper_Module
hardware.deviceTree = {
enable = true;
overlays = [
{
name = "spi-on";
dtsText = ''
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2711";
fragment@0 {
target = <&spi0>;
__overlay__ {
status = "okay";
};
};
};
'';
}
];
};
users.groups.spi = {};
users.groups.gpio = {};
services.udev.extraRules = ''
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660"
'';
}