drafted out virt home assistant

This commit is contained in:
Leyla Becker 2025-03-23 19:16:02 -05:00
parent ba5d5a1487
commit 77f1aa30b7
4 changed files with 270 additions and 75 deletions

View file

@ -91,10 +91,10 @@
};
};
};
home-assistant = {
enable = false;
subdomain = "home";
};
# home-assistant = {
# enable = false;
# subdomain = "home";
# };
adguardhome = {
enable = false;
};
@ -178,6 +178,12 @@
enable = true;
subdomain = "search";
};
virt-home-assistant = {
enable = false;
networkBridge = "bond0";
hostDevice = "0x10c4:0xea60";
};
};
# disable computer sleeping

View file

@ -8,7 +8,7 @@
./jellyfin.nix
./forgejo.nix
./searx.nix
./home-assistant.nix
./virt-home-assistant.nix
./adguardhome.nix
./immich.nix
];

View file

@ -1,6 +1,7 @@
{
lib,
config,
inputs,
...
}: let
configDir = "/var/lib/hass";
@ -16,81 +17,114 @@ in {
config = lib.mkIf config.host.home-assistant.enable (lib.mkMerge [
{
systemd.tmpfiles.rules = [
"f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
];
services.home-assistant = {
enable = true;
configDir = configDir;
extraComponents = [
"met"
"radio_browser"
"isal"
"zha"
"jellyfin"
"webostv"
"tailscale"
"syncthing"
"sonos"
"analytics_insights"
"unifi"
"openweathermap"
];
config = {
http = {
server_port = 8082;
use_x_forwarded_for = true;
trusted_proxies = ["127.0.0.1" "::1"];
ip_ban_enabled = true;
login_attempts_threshold = 10;
};
# recorder.db_url = "postgresql://@/${db_user}";
"automation manual" = [];
"automation ui" = "!include automations.yaml";
};
extraPackages = python3Packages:
with python3Packages; [
hassil
numpy
gtts
virtualisation.libvirt = {
swtpm.enable = true;
connections."qemu:///session" = {
networks = [
{
definition = inputs.nix-virt.lib.network.writeXML (inputs.nix-virt.lib.network.templates.bridge
{
uuid = "d57e37e2-311f-4e5c-a484-97c2210c2770";
subnet_byte = 71;
});
active = true;
}
];
domains = [
{
definition = inputs.nix-virt.lib.domain.writeXML (inputs.nix-virt.lib.domain.templates.linux
{
name = "Home Assistant";
uuid = "c5cc0efc-6101-4c1d-be31-acbba203ccde";
memory = {
count = 4;
unit = "GiB";
};
# storage_vol = {
# pool = "MyPool";
# volume = "Penguin.qcow2";
# };
});
}
];
};
host = {
reverse_proxy.subdomains.${config.host.home-assistant.subdomain} = {
target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
websockets.enable = true;
forwardHeaders.enable = true;
extraConfig = ''
add_header Upgrade $http_upgrade;
add_header Connection \"upgrade\";
proxy_buffering off;
proxy_read_timeout 90;
'';
};
};
# systemd.tmpfiles.rules = [
# "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
# ];
# services.home-assistant = {
# enable = true;
# configDir = configDir;
# extraComponents = [
# "met"
# "radio_browser"
# "isal"
# "zha"
# "jellyfin"
# "webostv"
# "tailscale"
# "syncthing"
# "sonos"
# "analytics_insights"
# "unifi"
# "openweathermap"
# ];
# config = {
# http = {
# server_port = 8082;
# use_x_forwarded_for = true;
# trusted_proxies = ["127.0.0.1" "::1"];
# ip_ban_enabled = true;
# login_attempts_threshold = 10;
# };
# # recorder.db_url = "postgresql://@/${db_user}";
# "automation manual" = [];
# "automation ui" = "!include automations.yaml";
# };
# extraPackages = python3Packages:
# with python3Packages; [
# hassil
# numpy
# gtts
# ];
# };
# host = {
# reverse_proxy.subdomains.${config.host.home-assistant.subdomain} = {
# target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
# websockets.enable = true;
# forwardHeaders.enable = true;
# extraConfig = ''
# add_header Upgrade $http_upgrade;
# add_header Connection \"upgrade\";
# proxy_buffering off;
# proxy_read_timeout 90;
# '';
# };
# };
}
(lib.mkIf config.host.impermanence.enable {
assertions = [
{
assertion = config.services.home-assistant.configDir == configDir;
message = "home assistant config directory does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = configDir;
user = "hass";
group = "hass";
}
];
};
# assertions = [
# {
# assertion = config.services.home-assistant.configDir == configDir;
# message = "home assistant config directory does not match persistence";
# }
# ];
# environment.persistence."/persist/system/root" = {
# enable = true;
# hideMounts = true;
# directories = [
# {
# directory = configDir;
# user = "hass";
# group = "hass";
# }
# ];
# };
})
]);
}

View file

@ -0,0 +1,155 @@
{
config,
lib,
pkgs,
...
}: {
options.services.virt-home-assistant = {
enable = lib.mkEnableOption "Wether to enable home assistant virtual machine";
networkBridge = lib.mkOption {
type = lib.types.str;
description = "what network bridge should we attach to the image";
};
hostDevice = lib.mkOption {
type = lib.types.str;
description = "what host devices should be attached to the image";
};
initialVersion = lib.mkOption {
type = lib.types.str;
description = "what home assistant image version should we pull for initial instal";
default = "15.0";
};
imageName = lib.mkOption {
type = lib.types.str;
description = "where should the image be installed to";
default = "home-assistant.qcow2";
};
installLocation = lib.mkOption {
type = lib.types.str;
description = "where should the image be installed to";
default = "/etc/hass";
};
virtualMachineName = lib.mkOption {
type = lib.types.str;
description = "what name should we give the virtual machine";
default = "home-assistant";
};
subdomain = lib.mkOption {
type = lib.types.str;
description = "subdomain of base domain that home-assistant will be hosted at";
default = "home-assistant";
};
};
config = lib.mkIf config.services.virt-home-assistant.enable (lib.mkMerge [
{
# environment.systemPackages = with pkgs; [
# virt-manager
# ];
# TODO: move this to external module and just have an assertion here that its enabled
# enable virtualization on the system
virtualisation = {
libvirtd = {
enable = true;
qemu.ovmf.enable = true;
};
};
# TODO: deactivation script?
# create service to install and start the container
systemd.services.virt-install-home-assistant = let
# TODO: all of these need to be escaped to be used in commands reliably
bridgedNetwork = config.services.virt-home-assistant.networkBridge;
hostDevice = config.services.virt-home-assistant.hostDevice;
virtualMachineName = config.services.virt-home-assistant.virtualMachineName;
imageName = config.services.virt-home-assistant.imageName;
installLocation = config.services.virt-home-assistant.installLocation;
installImage = "${installLocation}/${imageName}";
initialVersion = config.services.virt-home-assistant.initialVersion;
home-assistant-qcow2 = pkgs.fetchurl {
name = "home-assistant.qcow2";
url = "https://github.com/home-assistant/operating-system/releases/download/${initialVersion}/haos_ova-${initialVersion}.qcow2.xz";
hash = "sha256-V1BEjvvLNbMMKJVyMCmipjQ/3owoJteeVxoF9LDHo1U=";
postFetch = ''
cp $out src.xz
rm -r $out
${pkgs.xz}/bin/unxz src.xz --stdout > $out/${imageName}
'';
};
# Write a script to install the Home Assistant OS qcow2 image
virtInstallScript = pkgs.writeShellScriptBin "virt-install-hass" ''
# Copy the initial image out of the package store to the install location if we don't have one yet
if [ ! -f ${installImage} ]; then
cp ${home-assistant-qcow2} ${installLocation}
fi
# Check if VM already exists, and other pre-conditions
if ! ${pkgs.libvirt}/bin/virsh list --all | grep -q ${virtualMachineName}; then
${pkgs.virt-manager}/bin/virt-install --name ${virtualMachineName} \
--description "Home Assistant OS" \
--os-variant=generic \
--boot uefi \
--ram=2048 \
--vcpus=2 \
--import \
--disk ${installImage},bus=sata \
--network bridge=${bridgedNetwork} \
--host-device ${hostDevice} \
--graphics none
${pkgs.libvirt}/bin/virsh autostart ${virtualMachineName}
fi
'';
in {
description = "Install and start Home Assistant";
wantedBy = ["multi-user.target"];
after = ["local-fs.target"];
requires = ["libvirtd.service"];
serviceConfig.Type = "oneshot";
serviceConfig = {
ExecStart = "${virtInstallScript}/bin/virt-install-hass";
};
};
# TODO: figure out what we need to proxy to the virtual image
# host = {
# reverse_proxy.subdomains.${config.services.virt-home-assistant.subdomain} = {
# target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
# websockets.enable = true;
# forwardHeaders.enable = true;
# extraConfig = ''
# add_header Upgrade $http_upgrade;
# add_header Connection \"upgrade\";
# proxy_buffering off;
# proxy_read_timeout 90;
# '';
# };
# };
}
(lib.mkIf config.services.fail2ban.enable {
# TODO: figure out how to write a config for this, prob based on nginx proxy logs?
})
(lib.mkIf config.host.impermanence.enable {
# assertions = [
# {
# assertion = config.services.virt-home-assistant.installLocation == configDir;
# message = "home assistant install location does not match persistence";
# }
# ];
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.virt-home-assistant.installLocation;
}
];
};
})
]);
}