Add signal notifications & apps

Signed-off-by: Hadi <[email protected]>
This commit is contained in:
Hadi
2026-09-04 19:12:35 +02:00
parent 858b13ba37
commit 8e502f9701
7 changed files with 304 additions and 44 deletions
+2 -1
View File
@@ -22,7 +22,8 @@
../../server-modules/fail2ban.nix
../../server-modules/default-creds.nix
../../server-modules/gitea.nix
../../server-modules/home-assistant.nix
../../server-modules/signal-cli-rest-api.nix
../../server-modules/home-assistant
# You should let those lines as is
./hardware-configuration.nix
+5 -2
View File
@@ -9,6 +9,9 @@ signing-pub-key: ENC[AES256_GCM,data:CB7uU2Q4oTEKihpTIXGLaV0fJ1cv/p4oJJ5kjaU6BZi
cloudflared-token: ENC[AES256_GCM,data:7DwWFL6JbJXuc6fSyrZkMt/+DAcqDYBQOzJ1QhBL4yfCpQ57mjDV2beRZ8H7SuyoKY3caifHfAVVPDGG1YnffZ9anLRyDW383aHKmgaeztOXj44eVQcHi3evnSiH6h0HasiyO8pNzhnKpYew8CwHSvYZMraX3ITz8UKOTtsanO9/8ZD6Y9gyxfLoySqbeBy2x/0Ql5hrDfrX854C+vYuXM9VJbtQqMBa7+IBydUvddk=,iv:jZTk5ptuwc+1aP2rimqGnyCHR4/J+W4Kyn3LKXXkv/I=,tag:xr/LhoNH5aWdIJr0Py6nAg==,type:str]
gitea-mirror-token: ENC[AES256_GCM,data:qIcmwmqIilNtvJPwLMte09p0WAJIfFUl4ZjqoDyx+B9dAf946HTbjQ==,iv:cISz1e6rRlrXp93/GFGhHaeUM4AgdE3iFPxbHVJH950=,tag:Ue1cLdIx78L/G0PzE55hBw==,type:str]
github-mirror-token: ENC[AES256_GCM,data:Pty3c2YIZKXuz05j2m2g01cPVdk8nI1ftBzVSt44eXzNJxVEH8rwDw==,iv:hk/M7rp1RlMj0gxXGEESv+VqRZlls/9jpu0jfgq5X4I=,tag:sijScT21VHUpp4Yy6xwy5A==,type:str]
signal_sender_number: ENC[AES256_GCM,data:7eR112vBWgfsY5kX,iv:omEgSZflp/vk28PylbZrsNbdzv2McD3ljB0dr/VcYYk=,tag:x852Hc58eMmLkLLmOSkNdQ==,type:str]
h: ENC[AES256_GCM,data:roBRoH3sAPznAA==,iv:MRZRgKbEEjZosjRH35nYXg04WDXTGUizdVXyhcjQRB8=,tag:ishmf5EXF7UQFE0YoHNPjA==,type:str]
d: ENC[AES256_GCM,data:1XFzLM+rTmJukg==,iv:/tJ+1x7tdmY2+U+l4UPWaj4X49tanLVv/Re3OoEdClc=,tag:99GQ/1PukfQODPJgTlFWdw==,type:str]
sops:
age:
- enc: |
@@ -20,7 +23,7 @@ sops:
yefODJGVtHrOm3yeXC2xlTi3sFW+c5wUl2yPqddbvcBt5Ud/yd4iXQ==
-----END AGE ENCRYPTED FILE-----
recipient: age12yvtj49pfh3fqzqflscm0ek4yzrjhr6cqhn7x89gdxnlykq0xudq5c7334
lastmodified: "2026-09-01T18:31:15Z"
mac: ENC[AES256_GCM,data:ESW+fkZa/yKPIilkeOFBd1mIFl/vgWJpj1mrySjRZ2J9hcVzd02Yu0gG8qeu5b0SnRo4WimvZBfnKnN/E9frgiw+HT9nymVWW0dYOBd3jPRcPE0JkWTsZV0ajvic0o2mCp7/b7bjGoiYxAy/8mbBzKOuG5T3i+ohtumXZ8Y5OIU=,iv:vYjwKVm+UfYF//5VXIdVwRmKkK5AThmPcT7oaM367xs=,tag:UoDIkBxEc4/3PCDB5HKfdQ==,type:str]
lastmodified: "2026-09-04T16:48:30Z"
mac: ENC[AES256_GCM,data:DD4AcqD7rSzo1M/sUcW6SKQgeXFaGZ0z60HDUM1QyTaRDOo2/WCmNG3o3KowRLal/vd8ZxaX6aPmKFYZ4oxYGSvojMXKu2s/EPRtVlx3uv2al0tj8Zg5N1aWvxbrFLjNRb+dQ+8R47Fsmyl54haPz6gRX2qTiJrWL5/YflSDCH0=,iv:5QORWGYcH0NsJ/2+zgrIjHTJBrykpoha5I9thp/0taw=,tag:lKHj77KY9GtFg0VNNGl6mQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
-41
View File
@@ -1,41 +0,0 @@
{config, ...}: {
services.home-assistant = {
enable = true;
openFirewall = true;
configWritable = true;
extraComponents = [
"default_config"
"met"
"esphome"
"hue"
"matter"
"thread"
];
config = {
default_config = {};
http = {
trusted_proxies = ["127.0.0.1" "::1"];
use_x_forwarded_for = true;
};
scene = "!include scenes.yaml";
automation = "!include automations.yaml";
script = "!include scripts.yaml";
};
};
systemd.services.home-assistant.preStart = ''
for f in scenes.yaml automations.yaml scripts.yaml; do
path="${config.services.home-assistant.configDir}/$f"
[ -f "$path" ] || echo "[]" > "$path"
done
'';
services.matter-server = {
enable = true;
extraArgs.primary-interface = config.var.networkInterface;
};
networking.firewall.allowedUDPPorts = [5353];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."hass.${config.var.domain}" = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
}
+94
View File
@@ -0,0 +1,94 @@
{
config,
lib,
pkgs,
...
}: let
nixyTheme = import ./theme.nix {inherit config pkgs;};
plugins = import ./plugins.nix {inherit pkgs;};
signalRecipients = [
"h"
"d"
];
in {
sops.secrets =
{signal_sender_number.owner = "hass";}
// lib.genAttrs signalRecipients (_: {owner = "hass";});
sops.templates."home-assistant-secrets.yaml" = {
path = "${config.services.home-assistant.configDir}/secrets.yaml";
owner = "hass";
restartUnits = ["home-assistant.service"];
content =
"signal_sender: \"${config.sops.placeholder.signal_sender_number}\"\n"
+ lib.concatMapStrings (n: "${n}: \"${config.sops.placeholder.${n}}\"\n") signalRecipients;
};
services.home-assistant = {
enable = true;
openFirewall = true;
configWritable = true;
extraComponents = [
"default_config"
"met"
"esphome"
"hue"
"matter"
"thread"
"sonos"
"apple_tv"
"signal_messenger"
"manual"
"meteo_france"
"remote_calendar"
];
customComponents = plugins.customComponents;
customLovelaceModules = plugins.customLovelaceModules;
config = {
default_config = {};
http = {
trusted_proxies = ["127.0.0.1" "::1"];
use_x_forwarded_for = true;
};
scene = "!include scenes.yaml";
automation = "!include automations.yaml";
script = "!include scripts.yaml";
frontend.themes = nixyTheme;
notify =
(map (n: {
name = "signal_${n}";
platform = "signal_messenger";
url = config.services.signal-cli-rest-api.url;
number = "!secret signal_sender";
recipients = ["!secret ${n}"];
})
signalRecipients)
++ [
{
name = "signal_all";
platform = "signal_messenger";
url = config.services.signal-cli-rest-api.url;
number = "!secret signal_sender";
recipients = map (n: "!secret ${n}") signalRecipients;
}
];
};
};
systemd.services.home-assistant.preStart = ''
for f in scenes.yaml automations.yaml scripts.yaml; do
path="${config.services.home-assistant.configDir}/$f"
[ -f "$path" ] || echo "[]" > "$path"
done
'';
services.matter-server = {
enable = true;
extraArgs.primary-interface = config.var.networkInterface;
};
networking.firewall.allowedUDPPorts = [5353];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."hass.${config.var.domain}" = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
}
+45
View File
@@ -0,0 +1,45 @@
# Custom (non-core) Home Assistant integrations built from source.
{pkgs, ...}: let
idfm-api = pkgs.home-assistant.python3Packages.buildPythonPackage rec {
pname = "idfm-api";
version = "1.3.1";
pyproject = true;
src = pkgs.fetchurl {
url = "https://files.pythonhosted.org/packages/source/i/idfm-api/idfm_api-${version}.tar.gz";
hash = "sha256-j9whladp+nTUd6dnB0EybOiG2xQ7OyLf6LN3MpzjZv0=";
};
build-system = [pkgs.home-assistant.python3Packages.setuptools];
dependencies = with pkgs.home-assistant.python3Packages; [aiohttp async-timeout];
doCheck = false;
};
idfm = pkgs.buildHomeAssistantComponent {
owner = "droso-hass";
domain = "idfm";
version = "2.3.1";
src = pkgs.fetchFromGitHub {
owner = "droso-hass";
repo = "idfm";
tag = "v2.3.1";
hash = "sha256-Zlgd6TXxipcfUcdOyOpLcchM9DO84jCCtREFgbGRcY4=";
};
dependencies = [idfm-api];
};
atomic-calendar-revive = pkgs.stdenvNoCC.mkDerivation {
pname = "atomic-calendar-revive";
version = "10.3.1";
src = pkgs.fetchurl {
url = "https://github.com/totaldebug/atomic-calendar-revive/releases/download/v10.3.1/atomic-calendar-revive.js";
hash = "sha256-F4vXHQRAsiaK/ylL4X5d70TBxukMAqeQixb6XE68yZo=";
};
dontUnpack = true;
installPhase = ''
mkdir $out
cp $src $out/atomic-calendar-revive.js
'';
};
in {
customComponents = [idfm];
customLovelaceModules = [atomic-calendar-revive];
}
+128
View File
@@ -0,0 +1,128 @@
{
config,
pkgs,
...
}: let
b = config.stylix.base16Scheme;
hex = shade: "#${shade}";
rgbTriplet = shade: let
d = c:
if c == "a"
then 10
else if c == "b"
then 11
else if c == "c"
then 12
else if c == "d"
then 13
else if c == "e"
then 14
else if c == "f"
then 15
else pkgs.lib.toInt c;
h = pkgs.lib.toLower shade;
byte = pos: toString (d (builtins.substring pos 1 h) * 16 + d (builtins.substring (pos + 1) 1 h));
in "${byte 0}, ${byte 2}, ${byte 4}";
in {
"Nixy" = {
# Main interface colors
"primary-color" = hex b.base0D;
"accent-color" = "var(--primary-color)";
"primary-background-color" = hex b.base00;
"secondary-background-color" = hex b.base01;
"divider-color" = hex b.base02;
# Text
"primary-text-color" = hex b.base05;
"text-primary-color" = "var(--primary-text-color)";
"secondary-text-color" = hex b.base04;
"disabled-text-color" = hex b.base03;
# Header / sidebar
"app-header-background-color" = hex b.base01;
"app-header-text-color" = "var(--primary-text-color)";
"app-header-selection-bar-color" = "var(--primary-color)";
"app-header-edit-background-color" = hex b.base02;
"sidebar-icon-color" = "var(--secondary-text-color)";
"sidebar-text-color" = "var(--primary-text-color)";
"sidebar-background-color" = hex b.base01;
"sidebar-selected-icon-color" = "var(--primary-color)";
"sidebar-selected-text-color" = "var(--primary-color)";
# Buttons / icons / state
"paper-item-icon-color" = "var(--secondary-text-color)";
"paper-item-icon-active-color" = "var(--primary-color)";
"mdc-button-outline-color" = "var(--primary-color)";
"state-icon-color" = "var(--paper-item-icon-color)";
"state-icon-active-color" = "var(--paper-item-icon-active-color)";
"state-icon-unavailable-color" = "var(--disabled-text-color)";
# Sliders
"paper-slider-knob-color" = "var(--primary-color)";
"paper-slider-knob-start-color" = "var(--primary-color)";
"paper-slider-pin-color" = "var(--primary-color)";
"paper-slider-active-color" = "var(--primary-color)";
"paper-slider-secondary-color" = "var(--secondary-background-color)";
# Labels / badges
"label-badge-background-color" = "var(--secondary-background-color)";
"label-badge-text-color" = "var(--primary-text-color)";
"label-badge-red" = hex b.base08;
"label-badge-green" = hex b.base0B;
"label-badge-blue" = hex b.base0D;
"label-badge-yellow" = hex b.base0A;
"label-badge-gray" = "var(--secondary-text-color)";
# Cards / surfaces
"card-background-color" = hex b.base01;
"ha-card-background" = "var(--card-background-color)";
"ha-card-border-color" = hex b.base02;
"paper-dialog-background-color" = "var(--card-background-color)";
"paper-listbox-background-color" = "var(--card-background-color)";
"paper-card-background-color" = "var(--card-background-color)";
"table-row-background-color" = "var(--primary-background-color)";
"table-row-alternative-background-color" = "var(--secondary-background-color)";
"data-table-background-color" = "var(--primary-background-color)";
"material-background-color" = "var(--primary-background-color)";
"material-secondary-background-color" = "var(--secondary-background-color)";
"markdown-code-background-color" = hex b.base01;
"code-editor-background-color" = hex b.base01;
# Switches / toggles
"switch-checked-button-color" = "var(--primary-color)";
"switch-checked-track-color" = "var(--primary-color)";
"switch-unchecked-button-color" = "var(--secondary-text-color)";
"switch-unchecked-track-color" = "var(--disabled-text-color)";
"paper-toggle-button-checked-button-color" = "var(--switch-checked-button-color)";
"paper-toggle-button-checked-bar-color" = "var(--switch-checked-track-color)";
"paper-toggle-button-unchecked-button-color" = "var(--switch-unchecked-button-color)";
"paper-toggle-button-unchecked-bar-color" = "var(--switch-unchecked-track-color)";
"mdc-checkbox-unchecked-color" = "var(--secondary-text-color)";
"mdc-checkbox-disable-color" = "var(--disabled-text-color)";
"mdc-theme-primary" = "var(--primary-color)";
"mdc-theme-secondary" = "var(--primary-color)";
"mdc-theme-background" = "var(--primary-background-color)";
"mdc-theme-surface" = "var(--card-background-color)";
"mdc-theme-on-primary" = hex b.base00;
"mdc-theme-on-secondary" = hex b.base00;
"mdc-theme-on-surface" = "var(--primary-text-color)";
"mdc-theme-text-primary-on-background" = "var(--primary-text-color)";
"mdc-theme-text-secondary-on-background" = "var(--secondary-text-color)";
"mdc-theme-text-hint-on-background" = "var(--secondary-text-color)";
"mdc-theme-text-disabled-on-background" = "var(--disabled-text-color)";
"mdc-theme-text-icon-on-background" = "var(--secondary-text-color)";
"mdc-select-fill-color" = "var(--secondary-background-color)";
"mdc-select-ink-color" = "var(--primary-text-color)";
"mdc-select-label-ink-color" = "var(--secondary-text-color)";
"mdc-select-idle-line-color" = "var(--secondary-text-color)";
"mdc-select-dropdown-icon-color" = "var(--secondary-text-color)";
"mdc-select-hover-line-color" = "var(--primary-color)";
"mdc-list-item-graphic-color" = "var(--secondary-text-color)";
"mdc-text-field-fill-color" = "var(--mdc-select-fill-color)";
"mdc-text-field-ink-color" = "var(--primary-text-color)";
"mdc-text-field-label-ink-color" = "var(--secondary-text-color)";
"mdc-text-field-idle-line-color" = "var(--secondary-text-color)";
"input-fill-color" = "var(--secondary-background-color)";
"input-dropdown-icon-color" = "var(--secondary-text-color)";
"input-ink-color" = "var(--primary-text-color)";
"input-label-ink-color" = "var(--secondary-text-color)";
"input-idle-line-color" = "var(--secondary-text-color)";
"input-hover-line-color" = "var(--primary-color)";
"rgb-primary-text-color" = rgbTriplet b.base05;
"rgb-primary-color" = rgbTriplet b.base0D;
"rgb-accent-color" = rgbTriplet b.base0D;
};
}
+30
View File
@@ -0,0 +1,30 @@
{lib, ...}: let
port = 8080;
in {
options.services.signal-cli-rest-api.url = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = "http://127.0.0.1:${toString port}";
description = "URL of the local signal-cli-rest-api instance.";
};
config = {
virtualisation.docker.enable = true;
systemd.tmpfiles.rules = ["d /var/lib/signal-cli-rest-api 0750 root root -"];
virtualisation.oci-containers = {
backend = "docker";
containers.signal-cli-rest-api = {
image = "bbernhard/signal-cli-rest-api:0.98";
# json-rpc keeps one persistent signal-cli daemon instead of spawning
# a fresh process per API call; native mode serializes calls behind a
# file lock and each spawn takes ~40s, causing timeouts under any
# concurrent access (e.g. Home Assistant retries).
environment.MODE = "json-rpc";
ports = ["127.0.0.1:${toString port}:8080"];
volumes = ["/var/lib/signal-cli-rest-api:/home/.local/share/signal-cli"];
};
};
};
}