Merge remote-tracking branch 'gum/master'

This commit is contained in:
lassulus 2023-06-19 03:25:39 +02:00
commit 139799c53c
120 changed files with 2149 additions and 2027 deletions

View File

@ -104,7 +104,8 @@ in {
nets = {
retiolum.ip4.addr = "10.243.0.91";
wiregrill = {
# defaults
ip4.addr = "10.243.245.6";
aliases = [ "x.w" ];
};
};
@ -120,6 +121,12 @@ in {
ci = true;
syncthing.id = "Y5OTK3S-JOJLAUU-KTBXKUW-M7S5UEQ-MMQPUK2-7CXO5V6-NOUDLKP-PRGAFAK";
nets = {
wiregrill = {
aliases = ["omo.w" "hass.omo.w" "jelly.omo.w" "jelly.makefu.w" ];
ip6.addr = (krebs.genipv6 "wiregrill" "makefu" { hostName = "omo"; }).address;
ip4.addr = "10.244.245.5";
};
retiolum = {
ip4.addr = "10.243.0.89";
aliases = [
@ -239,6 +246,7 @@ in {
play.work.euer IN A ${nets.internet.ip4.addr}
ul.work.euer IN A ${nets.internet.ip4.addr}
music.euer IN A ${nets.internet.ip4.addr}
ntfy.euer IN A ${nets.internet.ip4.addr}
'';
};
nets = rec {

View File

@ -1 +1 @@
Ed25519PublicKey = lKMWnuEVjcSoSEUWrj+51pwDQrQj2TqloL3aBKVWBbO
lKMWnuEVjcSoSEUWrj+51pwDQrQj2TqloL3aBKVWBbO

View File

@ -0,0 +1 @@
JmcpzkwgKymVecZqaV0ODQactoVwGGlEHcfYIOCkx3A=

View File

@ -35,12 +35,13 @@ in
'';
networking = {
firewall.enable = true;
interfaces.et0.ipv4.addresses = [
{
address = shack-ip;
prefixLength = 20;
}
];
interfaces.et0.useDHCP = true;
#interfaces.et0.ipv4.addresses = [
# {
# address = shack-ip;
# prefixLength = 20;
# }
#];
defaultGateway = "10.42.0.1";
nameservers = [ "10.42.0.100" "10.42.0.200" ];

View File

@ -46,10 +46,8 @@
# light.shack web-ui
<stockholm/krebs/2configs/shack/light.shack.nix> #light.shack
# powerraw usb serial to mqtt and raw socket
<stockholm/krebs/2configs/shack/powerraw.nix> # powerraw.shack standby.shack
# send power stats to s3
<stockholm/krebs/2configs/shack/s3-power.nix> # powerraw.shack must be available
# fetch the u300 power stats
<stockholm/krebs/2configs/shack/power/u300-power.nix>
{ # do not log to /var/spool/log

View File

@ -7,6 +7,7 @@ in {
SUBSYSTEM=="net", ATTR{address}=="8c:70:5a:b2:84:58", NAME="wl0"
SUBSYSTEM=="net", ATTR{address}=="3c:97:0e:07:b9:14", NAME="${ext-if}"
'';
networking.wireless.enable = true;
networking = {
firewall.enable = true;
firewall.allowedTCPPorts = [ 80 443 8088 8086 8083 5901 ];

View File

@ -1,23 +0,0 @@
# needs:
# binary_sensor.lounge_ampel_status
# light.lounge_ampel_licht_rot
let
glados = import ../lib;
in
{
services.home-assistant.config.automation =
[
{
alias = "Ampel Rotes Licht";
initial_state = true;
trigger = {
platform = "state";
entity_id = "binary_sensor.lounge_ampel_status";
};
action = { service = "light.turn_on";
data.entity_id = "light.lounge_ampel_licht_rot";
};
}
];
}

View File

@ -1,28 +0,0 @@
Willkommen werter Keyholder {{ states("sensor.keyholder") }} in deinem Lieblingshackerspace.
Es ist {{states("sensor.fablab_feinstaub_temperature") | round(1) | replace('.',' Komma ')}} Grad {% if states("sensor.fablab_feinstaub_temperature")|float > 25 %}heiss{%elif states("sensor.fablab_feinstaub_temperature")|float > 15%}warm{%else%}kalt{%endif%} bei {% if states(" sensor.rz_feinstaub_humidity") | int <45 %}trockenen{% elif states(" sensor.rz_feinstaub_humidity") | int <65 %}angenehmen{%else%}feuchten{%endif%} {{states(" sensor.rz_feinstaub_humidity") | int }} Prozent Luftfeuchtigkeit.
{% if (states("sensor.fullstand_mate_1")|int == 0) and
states("sensor.fullstand_mate_2")|int == 0 %}ES IST MAHTECALYPSE, BEIDE MAHTESCHÄCHTE SIND LEER! {%if states("sensor.fullstand_mate_cola")| int == 0%} UND SOGAR DIE COLA IST ALLE. Ihr seid sowas von am Arsch!{%else%}Zum Glück gibt es noch Cola, Phew!{%endif%}
{% elif (states("sensor.fullstand_mate_1")|int + states("sensor.fullstand_mate_2")|int) < 5 %}
Der Mahtestand im Automaten ist mit {{states("sensor.fullstand_mate_1")|int + states("sensor.fullstand_mate_2")|int }} verbleibenden Flaschen kritisch!
{% else %}
Im Automaten sind noch {{states("sensor.fullstand_mate_1")|int + states("sensor.fullstand_mate_2")|int }} Flaschen Mahte und {{states("sensor.fullstand_mate_cola")}} Flaschen Cola.
{%endif%}
Die Wettervorhersage: {{states("sensor.dark_sky_hourly_summary")}} Aktuell {{states("sensor.dark_sky_summary")}} bei {{states("sensor.dark_sky_temperature") | round(1) | replace('.',' Komma ')}} Grad.
Der Stromverbrauch liegt bei {{ (( states("sensor.l1_power")|int + states("sensor.l2_power")|int + states("sensor.l3_power")|int ) / 1000 )| round(1) | replace('.',' Komma ')}} Kilowatt.
Im Fablab ist die Feinstaubbelastung {% if states("sensor.fablab_particulate_matter_2_5um_concentration") | float > 50 %}hoch!{%elif states("sensor.fablab_particulate_matter_2_5um_concentration") | float > 25 %}mäßig.{% else %}gering.{%endif%}
{% if is_state("binary_sensor.door_rzl",'on') and is_state("binary_sensor.door_entropia",'on') %}
Das Raumzeitlabor und Entropia haben geöffnet.
{% elif is_state("binary_sensor.door_rzl",'off') and is_state("binary_sensor.door_entropia",'off') %}
Das Raumzeitlabor und Entropia haben geschlossen.
{% elif is_state("binary_sensor.door_rzl",'on') and is_state("binary_sensor.door_entropia",'off') %}
Das Raumzeitlabor hat geöffnet und Entropia hat geschlossen.
{% elif is_state("binary_sensor.door_rzl",'off') and is_state("binary_sensor.door_entropia",'on') %}
Das Raumzeitlabor hat geschlossen und Entropia hat geöffnet.
{%endif%}
Die Glados Hackerspace Automation wünscht dir und allen Anwesenden einen produktiven und angenehmen Aufenthalt!

View File

@ -1,24 +0,0 @@
# needs:
# light.fablab_led
{
services.home-assistant.config.automation =
[
{ alias = "State on HA start-up";
trigger = {
platform = "homeassistant";
event = "start";
};
# trigger good/bad air
action = [
{ service = "light.turn_on";
data = {
entity_id = "light.fablab_led";
effect = "Rainbow";
color_name = "purple";
};
}
];
}
];
}

View File

@ -1,32 +0,0 @@
# Needs:
# sun.sunset
# switch.lounge_diskoschalter_relay
let
glados = import ../lib;
disko_schalter = "switch.lounge_diskoschalter_relay";
player = "media_player.lounge";
in
{
services.home-assistant.config.automation =
[
{ alias = "Party um 21 Uhr";
trigger = {
platform = "sun";
event = "sunset";
};
action =
( glados.say.kiosk "Die Sonne geht unter. Und jetzt geht die Party im shack erst richtig los. Partybeleuchtung, aktiviert!" )
++
[
{
service = "homeassistant.turn_on";
entity_id = disko_schalter;
}
{
service = "media_player.turn_on";
data.entity_id = player;
} # TODO: also start playlist if nothing is running?
];
}
];
}

View File

@ -1,100 +0,0 @@
# needs:
# binary_sensor.portal_lock
# sensor.keyholder
# media_player.lounge
# additional state required on:
# mpd.shack:
# playlist "ansage"
# playlist "lassulus"
# lounge.kiosk.shack:
# playlist "ansage"
let
glados = import ../lib;
in
{
services.home-assistant.config.automation =
[
{
alias = "Bedanken bei Übernahme von Key";
initial_state = true;
trigger = {
platform = "state";
entity_id = "sensor.keyholder";
};
condition = {
condition = "template";
value_template = "{{ (trigger.from_state.state != 'No Keyholder') and (trigger.from_state.state != 'No Keyholder') }}";
};
action = glados.say.kiosk "Danke {{ trigger.to_state.state }} für das Übernehmen des Keys von {{ trigger.from_state.state }}";
}
{
alias = "Keyholder Begrüßen wenn MPD hoch fährt";
initial_state = true;
trigger = {
platform = "state";
from = "unavailable";
entity_id = "media_player.kiosk";
};
action = glados.say.kiosk (builtins.readFile ./announcement.j2);
}
{
alias = "Start Music on portal lock on";
trigger = {
platform = "state";
entity_id = "binary_sensor.portal_lock";
to = "on";
for.seconds = 30;
};
condition = {
condition = "and";
conditions =
[
{ # only start if a keyholder opened the door and if the lounge mpd is currently not playing anything
condition = "template";
value_template = "{{ state('sensor.keyholder') != 'No Keyholder' }}";
}
{
condition = "state";
entity_id = "media_player.lounge";
state = "idle";
}
];
};
action = [
{
service = "media_player.volume_set";
data = {
entity_id = "media_player.lounge";
volume_level = 1.0;
};
}
{
service = "media_player.play_media";
data = {
entity_id = "media_player.lounge";
media_content_type = "playlist";
media_content_id = "ansage";
};
}
{ delay.seconds = 8.5; }
{
service = "media_player.volume_set";
data = {
entity_id = "media_player.lounge";
volume_level = 0.6;
};
}
{
service = "media_player.play_media";
data = {
entity_id = "media_player.lounge";
media_content_type = "playlist";
media_content_id = "lassulus";
};
}
];
}
];
}

View File

@ -1,12 +1,33 @@
{ config, pkgs, lib, ... }:
let
unstable = import (pkgs.fetchFromGitHub {
owner = "nixos";
repo = "nixpkgs";
rev = (lib.importJSON ../../../nixpkgs-unstable.json).rev;
sha256 = (lib.importJSON ../../../nixpkgs-unstable.json).sha256;
}) {};
kodi-host = "192.168.8.11";
confdir = "/var/lib/homeassistant-docker";
in {
imports = [
];
# networking.firewall.allowedTCPPorts = [ 8123 ];
virtualisation.oci-containers.containers.hass = {
image = "homeassistant/home-assistant:latest";
environment = {
TZ = "Europe/Berlin";
# TODO create unique users
PUID = toString config.users.users.news_container.uid;
PGID = toString config.users.groups.news_container.gid;
UMASK = "007";
};
extraOptions = ["--net=host" ];
volumes = [
"${confdir}:/config"
#"${confdir}/docker-run:/etc/services.d/home-assistant/run:"
];
};
systemd.tmpfiles.rules = [
#"f ${confdir}/docker-run 0770 kiosk kiosk - -"
# TODO:
"d ${confdir} 0770 news_container news_container - -"
];
services.nginx.virtualHosts."hass.shack" = {
serverAliases = [ "glados.shack" ];
locations."/" = {
@ -23,127 +44,4 @@ in {
'';
};
};
imports = [
./multi/shackopen.nix
./multi/wasser.nix
./multi/schlechte_luft.nix
./multi/rollos.nix
./switch/power.nix
./sensors/power.nix
./sensors/mate.nix
./sensors/darksky.nix
./sensors/spaceapi.nix
./sensors/sensemap.nix
./automation/shack-startup.nix
./automation/party-time.nix
./automation/hass-restart.nix
./automation/ampel.nix
];
services.home-assistant =
{
enable = true;
package = unstable.home-assistant.overrideAttrs (old: {
doInstallCheck = false;
});
config = {
homeassistant = {
name = "Glados";
time_zone = "Europe/Berlin";
latitude = "48.8265";
longitude = "9.0676";
elevation = 303;
auth_providers = [
{ type = "homeassistant";}
{ type = "trusted_networks";
trusted_networks = [
"127.0.0.1/32"
"10.42.0.0/16"
"::1/128"
"fd00::/8"
];
}
];
};
# https://www.home-assistant.io/components/influxdb/
influxdb = {
database = "glados";
host = "influx.shack";
component_config_glob = {
"sensor.*particulate_matter_2_5um_concentration".override_measurement = "2_5um particles";
"sensor.*particulate_matter_10_0um_concentration".override_measurement ="10um particles";
};
tags = {
instance = "wolf";
source = "glados";
};
};
esphome = {};
api = {};
mqtt = {
broker = "localhost";
port = 1883;
client_id = "home-assistant";
keepalive = 60;
protocol = 3.1;
discovery = true; #enable esphome discovery
discovery_prefix = "homeassistant";
birth_message = {
topic = "glados/hass/status/LWT";
payload = "Online";
qos = 1;
retain = true;
};
will_message = {
topic = "glados/hass/status/LWT";
payload = "Offline";
qos = 1;
retain = true;
};
};
light = [];
media_player = [
{ platform = "mpd";
name = "lounge";
host = "lounge.mpd.shack";
}
{ platform = "mpd";
name = "kiosk";
#host = "lounge.kiosk.shack";
host = "kiosk.shack";
}
];
camera = [];
frontend = { };
config = { };
sun = {};
http = {
base_url = "http://hass.shack";
use_x_forwarded_for = true;
trusted_proxies = [ "127.0.0.1" "::1" ];
};
#conversation = {};
history = {};
logbook = {};
#recorder = {};
logger.default = "info";
tts = [
{ platform = "google_translate";
service_name = "say";
language = "de";
cache = true;
time_memory = 57600;
base_url = "http://hass.shack";
}
];
device_tracker = [];
};
};
}

View File

@ -1,27 +0,0 @@
{ lib
, buildPythonPackage
, fetchPypi
, requests
}:
buildPythonPackage rec {
pname = "gtts-token";
version = "1.1.3";
src = fetchPypi {
pname = "gTTS-token";
inherit version;
sha256 = "9d6819a85b813f235397ef931ad4b680f03d843c9b2a9e74dd95175a4bc012c5";
};
propagatedBuildInputs = [
requests
];
meta = with lib; {
description = "Calculates a token to run the Google Translate text to speech";
homepage = https://github.com/boudewijn26/gTTS-token;
license = licenses.mit;
# maintainers = [ maintainers. ];
};
}

View File

@ -1,33 +0,0 @@
{ lib
, buildPythonPackage
, fetchpatch
, fetchPypi
, aiohttp
, async-timeout
}:
buildPythonPackage rec {
pname = "pyhaversion";
version = "2.2.1";
src = fetchPypi {
inherit pname version;
sha256 = "72b65aa25d7b2dbb839a4d0218df2005c2335e93526035904d365bb668030b9f";
};
patches = [
(fetchpatch { url = "https://github.com/makefu/pyhaversion/commit/f3bdc38970272cd345c2cfbde3037ea492ca27c4.patch";
sha256 =
"1rhq4z7mdgnwhwpf5fmarnbc1ba3qysk1wqjdr0hvbzi8vmvbfcc";})
];
doCheck = false;
propagatedBuildInputs = [
aiohttp
async-timeout
];
meta = with lib; {
description = "";
homepage = https://github.com/ludeeus/pyhaversion;
# maintainers = [ maintainers. ];
};
}

View File

@ -1,66 +0,0 @@
let
prefix = "glados";
in
{
say = let
# returns a list of actions to be performed on an mpd to say something
tts = { message, entity }:
[
{
service = "media_player.turn_on";
data.entity_id = "media_player.${entity}";
}
{ service = "media_player.play_media";
data = {
entity_id = "media_player.${entity}";
media_content_type = "playlist";
media_content_id = "ansage";
};
}
{
service = "media_player.turn_on";
data.entity_id = "media_player.${entity}";
}
{ delay.seconds = 4.5; }
{ service = "tts.say";
entity_id = "media_player.${entity}";
data_template = {
inherit message;
language = "de";
};
}
];
in
{
lounge = message: tts {
inherit message;
entity = "lounge";
};
herrenklo = message: tts {
inherit message;
entity = "herrenklo";
};
kiosk = message: tts {
inherit message;
entity = "kiosk";
};
};
tasmota =
{
plug = {host, name ? host, topic ? host}:
{
platform = "mqtt";
inherit name;
state_topic = "sonoff/stat/${topic}/POWER1";
command_topic = "sonoff/cmnd/${topic}/POWER1";
availability_topic = "sonoff/tele/${topic}/LWT";
payload_on= "ON";
payload_off= "OFF";
payload_available= "Online";
payload_not_available= "Offline";
retain = false;
qos = 1;
};
};
}

View File

@ -1,59 +0,0 @@
#
let
glados = import ../lib;
tempsensor = "sensor.dark_sky_temperature";
all_covers = [
"cover.crafting_rollo"
"cover.elab_rollo"
"cover.or2_rollo"
"cover.retroraum_rollo"
];
in
{
services.home-assistant.config =
{
automation =
[
{ alias = "Rollos fahren Runter";
trigger = [
{
platform = "numeric_state";
entity_id = tempsensor;
above = 25;
for = "00:30:00";
}
];
condition =
[
{
condition = "state";
entity_id = "sun.sun";
state = "above_horizon";
}
];
action =
[
{ service = "cover.close_cover";
entity_id = all_covers;
}
];
}
{ alias = "Rollos fahren Hoch";
trigger = [
{
platform = "sun";
event = "sunset";
}
];
condition = [ ];
action =
[
{ service = "cover.open_cover";
entity_id = all_covers;
}
];
}
];
};
}

View File

@ -1,109 +0,0 @@
let
glados = import ../lib;
feinstaub_sensor = "sensor.fablab_particulate_matter_2_5um_concentration";
ledring = "light.fablab_led_ring";
in
{
services.home-assistant.config =
{
automation =
[
{ alias = "Gute Luft Fablab";
trigger = [
{
platform = "numeric_state";
entity_id = feinstaub_sensor;
below = 3;
}
];
action =
[
{ service = "light.turn_on";
data = {
entity_id = ledring;
effect = "Twinkle";
color_name = "green";
};
}
];
}
{ alias = "mäßige Luft Fablab";
trigger = [
{
platform = "numeric_state";
above = 3;
below = 10;
entity_id = feinstaub_sensor;
}
];
action =
[
{ service = "light.turn_on";
data = {
entity_id = ledring;
effect = "Twinkle";
color_name = "yellow";
};
}
];
}
{ alias = "schlechte Luft Fablab";
trigger = [
{
platform = "numeric_state";
above = 10;
entity_id = feinstaub_sensor;
}
];
action =
[
{ service = "light.turn_on";
data = {
entity_id = ledring;
effect = "Fireworks";
color_name = "red";
};
}
];
}
{ alias = "Luft Sensor nicht verfügbar";
trigger = [
{
platform = "state";
to = "unavailable";
entity_id = feinstaub_sensor;
}
];
action =
[
{ service = "light.turn_on";
data = {
entity_id = ledring;
effect = "Rainbow";
color_name = "blue";
};
}
];
}
{ alias = "Fablab Licht Reboot";
trigger = [
{
platform = "state";
from = "unavailable";
entity_id = ledring;
}
];
action =
[
{ service = "light.turn_on";
data = {
entity_id = ledring;
effect = "Rainbow";
color_name = "orange";
};
}
];
}
];
};
}

View File

@ -1,26 +0,0 @@
{
services.home-assistant.config =
{
binary_sensor = [
{ platform = "mqtt";
name = "Portal Lock";
device_class = "door";
state_topic = "portal/gateway/status";
availability_topic = "portal/gateway/lwt";
payload_on = "open";
payload_off = "closed";
payload_available = "online";
payload_not_available = "offline";
}
];
sensor = [
{ platform = "mqtt";
name = "Keyholder";
state_topic = "portal/gateway/keyholder";
availability_topic = "portal/gateway/lwt";
payload_available = "online";
payload_not_available = "offline";
}
];
};
}

View File

@ -1,113 +0,0 @@
# uses:
# switch.crafting_giesskanne_relay
let
glados = import ../lib;
seconds = 20;
wasser = "switch.crafting_giesskanne_relay";
brotbox = {
minutes = 10;
pump = "switch.crafting_brotbox_pumpe";
sensor = "sensor.statistics_for_sensor_crafting_brotbox_soil_moisture";
};
in
{
services.home-assistant.config =
{
sensor = map ( entity_id: {
platform = "statistics";
name = "Statistics for ${entity_id}";
inherit entity_id;
max_age.minutes = "60";
sampling_size = 1000;
}) ["sensor.crafting_brotbox_soil_moisture"];
automation =
[
### Brotbox #####
#{ alias = "Brotbox: water for ${toString brotbox.minutes} minutes every hour";
# trigger =
# { # Trigger once every hour at :42
# platform = "time_pattern";
# minutes = 42;
# };
# condition = {
# condition = "numeric_state";
# entity_id = brotbox.sensor;
# value_template = "{{ state_attr('${brotbox.sensor}', 'median') }}";
# below = 75;
# };
# action =
# [
# {
# service = "homeassistant.turn_on";
# entity_id = brotbox.pump;
# }
# { delay.minutes = brotbox.minutes; }
# {
# service = "homeassistant.turn_off";
# entity_id = brotbox.pump ;
# }
# ];
#}
{ alias = "Brotbox: Always turn off water after ${toString (brotbox.minutes * 2)} minutes";
trigger =
{
platform = "state";
entity_id = brotbox.pump;
to = "on";
for.minutes = brotbox.minutes*2;
};
action =
{
service = "homeassistant.turn_off";
entity_id = brotbox.pump;
};
}
##### Kaffeemaschine
{ alias = "Water the plant for ${toString seconds} seconds";
trigger = [
{ # trigger at 20:00 no matter what
# TODO: retry or run only if switch.wasser is available
platform = "time";
at = "20:00:00";
}
];
action =
[
{
service = "homeassistant.turn_on";
entity_id = [
wasser
];
}
{ delay.seconds = seconds; }
{
service = "homeassistant.turn_off";
entity_id = [
wasser
];
}
];
}
{ alias = "Always turn off water after ${toString (seconds * 2)}seconds";
trigger = [
{
platform = "state";
entity_id = wasser;
to = "on";
for.seconds = seconds*2;
}
];
action =
[
{
service = "homeassistant.turn_off";
entity_id = [ wasser ];
}
];
}
];
};
}

View File

@ -1,24 +0,0 @@
{lib,...}:
{
services.home-assistant.config.sensor =
[
{ platform = "darksky";
api_key = lib.removeSuffix "\n"
(builtins.readFile <secrets/hass/darksky.apikey>);
language = "de";
monitored_conditions = [
"summary" "icon"
"nearest_storm_distance" "precip_probability"
"precip_intensity"
"temperature" # "temperature_high" "temperature_low"
"apparent_temperature"
"hourly_summary" # next 24 hours text
"humidity"
"pressure"
"uv_index"
];
units = "si" ;
scan_interval = "00:15:00";
}
];
}

View File

@ -1,20 +0,0 @@
let
fuellstand = name: id: {
platform = "rest";
resource = "https://ora5.tutschonwieder.net/ords/lick_prod/v1/get/fuellstand/1/${toString id}";
method = "GET";
name = "Füllstand ${name}";
value_template = "{{ value_json.fuellstand }}";
};
in
{
services.home-assistant.config.sensor =
[
(fuellstand "Wasser" 1)
(fuellstand "Mate Cola" 2)
(fuellstand "Apfelschorle" 3)
(fuellstand "Zitronensprudel" 4)
(fuellstand "Mate 1" 26)
(fuellstand "Mate 2" 27)
];
}

View File

@ -1,29 +0,0 @@
let
power_x = name: phase:
{ platform = "mqtt";
name = "${phase} ${name}";
state_topic = "/power/total/${phase}/${name}";
availability_topic = "/power/lwt";
payload_available = "Online";
payload_not_available = "Offline";
};
power_consumed =
{ platform = "mqtt";
name = "Power Consumed";
device_class = "power";
state_topic = "/power/total/consumed";
availability_topic = "/power/lwt";
payload_available = "Online";
payload_not_available = "Offline";
};
power_volt = power_x "Voltage";
power_watt = (power_x "Power") ;
power_curr = power_x "Current";
in
{
services.home-assistant.config.sensor =
(map power_volt [ "L1" "L2" "L3" ])
++ (map (x: ((power_watt x) // { device_class = "power"; })) [ "L1" "L2" "L3" ])
++ (map power_curr [ "L1" "L2" "L3" ])
++ [ power_consumed ];
}

View File

@ -1,9 +0,0 @@
{
services.home-assistant.config.air_quality =
[
{
platform = "opensensemap";
station_id = "56a0de932cb6e1e41040a68b";
}
];
}

View File

@ -1,55 +0,0 @@
{
services.home-assistant.config.binary_sensor =
[
{
platform = "rest";
resource = "https://spaceapi.afra-berlin.de/v1/status.json";
method = "GET";
name = "Door AFRA Berlin";
device_class = "door";
value_template = "{{ value_json.open }}";
}
{
platform = "rest";
resource = "http://club.entropia.de/spaceapi";
method = "GET";
name = "Door Entropia";
device_class = "door";
value_template = "{{ value_json.open }}";
}
{
platform = "rest";
resource = "http://www.c-base.org/status.json";
method = "GET";
name = "Door C-Base Berlin";
device_class = "door";
value_template = "{{ value_json.open }}";
}
{
platform = "rest";
resource = "https://status.raumzeitlabor.de/api/full.json";
method = "GET";
name = "Door RZL";
device_class = "door";
value_template = "{{ value_json.status }}";
}
{
platform = "rest";
resource = "https://datenobservatorium.de/";
method = "GET";
name = "Door Datenobservatorium";
device_class = "door";
value_template = "false";
scan_interval = 2592000;
}
{
platform = "rest";
resource = "https://infuanfu.de/";
method = "GET";
name = "Door Infuanfu";
device_class = "door";
value_template = "false";
scan_interval = 2592000;
}
];
}

View File

@ -1,6 +0,0 @@
{
controllers = {
host = "unifi.shack";
site = "shackspace";
};
}

View File

@ -1,44 +0,0 @@
# 1 - haupt
# 2 - dusche
# 3 - warmwasser
# 4 - or
# 5 - kueche
let
nodelight = type: ident: name: {
platform = "mqtt";
name = "${type} ${name}";
command_topic = "${type}/${toString ident}/command";
state_topic = "${type}/${toString ident}/state";
payload_on = "on";
payload_off = "off";
};
power = nodelight "power";
light = ident: name: { icon = "mdi:lightbulb";} // nodelight "light" ident name;
in
{
services.home-assistant.config.switch =
[
# These commands we see with a shutdown:
# power/143/state on
# power/142/state on
# power/141/state on
# power/142/state off
# power/141/state off
# power/10/state off
# power/main/state off
(power "10" "Hauptschalter")
(power 1 "Dusche") # ???
(power 2 "Warmwasser") # ???
(power 3 "Optionsräume") # ???
(power 4 "Küche") # ???
(light 1 "Decke Lounge 1")
(light 2 "Decke Lounge 2")
(light 3 "Decke Lounge 3")
(light 4 "Decke Lounge 4")
(light 5 "Decke Lounge 5")
(light 6 "Decke Lounge 6")
(light 7 "Decke Lounge 7")
(light 8 "Decke Lounge 8")
];
}

View File

@ -4,7 +4,18 @@ in {
networking.firewall.allowedTCPPorts = [ port ]; # legacy
services.nginx.virtualHosts."grafana.shack" = {
locations."/".proxyPass = "http://localhost:${toString port}";
locations."/" = {
proxyPass = "http://localhost:${toString port}";
extraConfig =''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
'';
};
};
services.grafana = {
enable = true;

View File

@ -15,6 +15,16 @@ in
'';
locations."/" = {
proxyPass = "http://localhost:${toString port}/";
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
'';
};
};
nixpkgs.overlays = [

View File

@ -0,0 +1,29 @@
{ pkgs, ... }:
let
src = pkgs.fetchFromGitHub {
repo = "shackstrom";
owner = "samularity";
rev = "adfbdc7d12000fbc9fd9367c8ef0a53b7d0a9fad";
hash = "sha256-77vSX2+1XXaBVgLka+tSEK/XYZASEk9iq+uEuO1aOUQ=";
};
pkg = pkgs.writers.writePython3 "test_python3" {
libraries = [ pkgs.python3Packages.requests pkgs.python3Packages.paho-mqtt ];
} (builtins.readFile "${src}/shackstrom.py");
in
{
systemd.services = {
u300-power = {
enable = true;
environment = {
DATA_URL = "http://10.42.20.255/csv.html";
BROKER = "mqtt.shack";
};
serviceConfig = {
Restart = "always";
ExecStart = pkg;
RestartSec = "15s";
};
wantedBy = [ "multi-user.target" ];
};
};
}

View File

@ -1,28 +1,12 @@
{ lib,... }:
let
disk_free_threshold = "10"; # at least this much free disk percentage
disk_free_threshold = "5"; # at least this much free disk percentage
in {
services.prometheus.rules = [(builtins.toJSON
{
groups = [
{ name = "shack-env";
rules = [
{
alert = "Wolf RootPartitionFull";
for = "30m";
expr = ''(node_filesystem_avail_bytes{alias="wolf.shack",mountpoint="/"} * 100) / node_filesystem_size_bytes{alias="wolf.shack",mountpoint="/"} < ${disk_free_threshold}'';
labels.severity = "warning";
annotations.summary = "{{ $labels.alias }} root disk full";
annotations.url = "http://grafana.shack/d/hb7fSE0Zz/shack-system-dashboard?orgId=1&var-job=node&var-hostname=All&var-node=wolf.shack:9100&var-device=All&var-maxmount=%2F&var-show_hostname=wolf";
annotations.description = ''The root disk of {{ $labels.alias }} has {{ $value | printf "%.2f" }}% free disk space (Threshold at ${disk_free_threshold}%). CI for deploying new configuration will seize working. Log in to the system and try to clean up the obsolete files on the machine. There are a couple of things you can do:
1. `nix-collect-garbage -d`
2. clean up the shack share folder in `/home/share`
3. check `du -hs /var/ | sort -h`.
4. run `docker system prune`
5. `find /var/lib/containers/news/var/lib/htgen-go/items -mtime +7 -delete;` to clean up the link shortener data
5. If you are really desperate run `du -hs / | sort -h` and go through the folders recursively until you've found something to delete
6. as a last resort the root disk can be expanded via `lvresize -L +10G /dev/pool/root && btrfs filesystem resize max /` '';
}
{
alert = "Puyak RootPartitionFull";
for = "30m";
@ -32,9 +16,8 @@ in {
annotations.url = "http://grafana.shack/d/hb7fSE0Zz/shack-system-dashboard?orgId=1&var-job=node&var-hostname=All&var-node=wolf.shack:9100&var-device=All&var-maxmount=%2F&var-show_hostname=puyak";
annotations.description = ''The root disk of {{ $labels.alias }} has {{ $value | printf "%.2f" }}% free disk space (Threshold at ${disk_free_threshold}%).Prometheus will not be able to create new alerts and CI for deploying new configuration will also seize working. Log in to the system and run `nix-collect-garbage -d` and if this does not help you can check `du -hs /var/ | sort -h`, run `docker system prune` or if you are really desperate run `du -hs / | sort -h` and go through the folders recursively until you've found something to delete'';
}
# wolf.shack is not worth supervising anymore
{
alert = "HostDown";
alert = "Infra01 down";
expr = ''up{alias="infra01.shack"} == 0'';
for = "5m";
labels.severity = "page";

View File

@ -0,0 +1,207 @@
import base64
import cgi
import json
import os
import re
import socket
import ssl
import sys
from http.server import BaseHTTPRequestHandler
from typing import List, Optional, Tuple
from urllib.parse import urlparse
DEBUG = os.environ.get("DEBUG") is not None
def _irc_send(
server: str,
nick: str,
channel: str,
sasl_password: Optional[str] = None,
server_password: Optional[str] = None,
tls: bool = True,
port: int = 6697,
messages: List[str] = [],
) -> None:
if not messages:
return
sock = socket.socket()
if tls:
sock = ssl.wrap_socket(
sock, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_TLSv1_2
)
def _send(command: str) -> int:
if DEBUG:
print(command)
return sock.send((f"{command}\r\n").encode())
def _pong(ping: str):
if ping.startswith("PING"):
sock.send(ping.replace("PING", "PONG").encode("ascii"))
recv_file = sock.makefile(mode="r")
print(f"connect {server}:{port}")
sock.connect((server, port))
if server_password:
_send(f"PASS {server_password}")
_send(f"USER {nick} 0 * :{nick}")
_send(f"NICK {nick}")
for line in recv_file.readline():
if re.match(r"^:[^ ]* (MODE|221|376|422) ", line):
break
else:
_pong(line)
if sasl_password:
_send("CAP REQ :sasl")
_send("AUTHENTICATE PLAIN")
auth = base64.encodebytes(f"{nick}\0{nick}\0{sasl_password}".encode("utf-8"))
_send(f"AUTHENTICATE {auth.decode('ascii')}")
_send("CAP END")
_send(f"JOIN :{channel}")
for m in messages:
_send(f"PRIVMSG {channel} :{m}")
_send("INFO")
for line in recv_file:
if DEBUG:
print(line, end="")
# Assume INFO reply means we are done
if "End of /INFO" in line:
break
else:
_pong(line)
sock.send(b"QUIT")
print("disconnect")
sock.close()
def irc_send(
url: str, notifications: List[str], password: Optional[str] = None
) -> None:
parsed = urlparse(f"{url}")
username = parsed.username or "prometheus"
server = parsed.hostname or "chat.freenode.net"
if parsed.fragment != "":
channel = f"#{parsed.fragment}"
else:
channel = "#krebs-announce"
port = parsed.port or 6697
if not password:
password = parsed.password
if len(notifications) == 0:
return
_irc_send(
server=server,
nick=username,
sasl_password=password,
channel=channel,
port=port,
messages=notifications,
tls=parsed.scheme == "irc+tls",
)
class PrometheusWebHook(BaseHTTPRequestHandler):
def __init__(
self,
irc_url: str,
conn: socket.socket,
addr: Tuple[str, int],
password: Optional[str] = None,
) -> None:
self.irc_url = irc_url
self.password = password
self.rfile = conn.makefile("rb")
self.wfile = conn.makefile("wb")
self.client_address = addr
self.handle()
# for testing
def do_GET(self) -> None:
if DEBUG:
print("GET: Request Received")
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"ok")
def do_POST(self) -> None:
if DEBUG:
print("POST: Request Received")
content_type, _ = cgi.parse_header(self.headers.get("content-type"))
# refuse to receive non-json content
if content_type != "application/json":
if DEBUG:
print(f"POST: wrong content type {content_type}")
self.send_response(400)
self.end_headers()
return
length = int(self.headers.get("content-length"))
payload = json.loads(self.rfile.read(length))
messages = []
for alert in payload["alerts"]:
description = alert["annotations"]["description"]
messages.append(f"{alert['status']}: {description}")
irc_send(self.irc_url, messages, password=self.password)
self.do_GET()
def systemd_socket_response() -> None:
irc_url = os.environ.get("IRC_URL", None)
if irc_url is None:
print(
"IRC_URL environment variable not set: i.e. IRC_URL=irc+tls://mic92-prometheus@chat.freenode.net/#krebs-announce",
file=sys.stderr,
)
sys.exit(1)
password = None
irc_password_file = os.environ.get("IRC_PASSWORD_FILE", None)
if irc_password_file:
with open(irc_password_file) as f:
password = f.read()
msgs = sys.argv[1:]
if msgs != []:
irc_send(irc_url, msgs, password=password)
return
nfds = os.environ.get("LISTEN_FDS", None)
if nfds is None:
print(
"LISTEN_FDS not set. Run me with systemd(TM) socket activation?",
file=sys.stderr,
)
sys.exit(1)
fds = range(3, 3 + int(nfds))
for fd in fds:
sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0)
try:
while True:
PrometheusWebHook(irc_url, *sock.accept(), password=password)
except BlockingIOError:
# no more connections
pass
if __name__ == "__main__":
if DEBUG:
print("Starting in DEBUG mode")
if len(sys.argv) == 3:
print(f"{sys.argv[1]} {sys.argv[2]}")
irc_send(sys.argv[1], [sys.argv[2]])
else:
systemd_socket_response()

View File

@ -0,0 +1,59 @@
{ config
, lib
, pkgs
, ...
}:
let
irc-alerts = pkgs.writers.writePython3 "irc-alerts" {
flakeIgnore = [ "E501" ];
} (builtins.readFile ./irc-alerts.py);
endpoints = {
binaergewitter = {
url = "irc+tls://puyak-alerts@irc.libera.chat:6697/#binaergewitter-alerts";
port = 9223;
};
};
in
{
systemd.sockets =
lib.mapAttrs'
(name: opts:
lib.nameValuePair "irc-alerts-${name}" {
description = "Receive http hook and send irc message for ${name}";
wantedBy = [ "sockets.target" ];
listenStreams = [ "[::]:${builtins.toString opts.port}" ];
}) endpoints;
systemd.services =
lib.mapAttrs'
(name: opts:
let
serviceName = "irc-alerts-${name}";
hasPassword = opts.passwordFile or null != null;
in
lib.nameValuePair serviceName {
description = "Receive http hook and send irc message for ${name}";
requires = [ "irc-alerts-${name}.socket" ];
serviceConfig =
{
Environment =
[
"IRC_URL=${opts.url}"
"DEBUG=y"
]
++ lib.optional hasPassword "IRC_PASSWORD_FILE=/run/${serviceName}/password";
DynamicUser = true;
User = serviceName;
ExecStart = irc-alerts;
}
// lib.optionalAttrs hasPassword {
PermissionsStartOnly = true;
ExecStartPre =
"${pkgs.coreutils}/bin/install -m400 "
+ "-o ${serviceName} -g ${serviceName} "
+ "${config.sops.secrets.prometheus-irc-password.path} "
+ "/run/${serviceName}/password";
RuntimeDirectory = serviceName;
};
}) endpoints;
}

View File

@ -3,6 +3,7 @@
{
imports = [
./alert-rules.nix
./irc-hooks.nix
];
networking = {
firewall.allowedTCPPorts = [
@ -129,11 +130,11 @@
"group_wait" = "30s";
"group_interval" = "2m";
"repeat_interval" = "4h";
"receiver" = "team-admins";
"receiver" = "shack-admins";
};
"receivers" = [
{
"name" = "team-admins";
"name" = "shack-admins";
"email_configs" = [ ];
"webhook_configs" = [
{

View File

@ -14,8 +14,15 @@ in {
#<stockholm/makefu/2configs/support-nixos.nix>
# <stockholm/makefu/2configs/homeautomation/default.nix>
# <stockholm/makefu/2configs/homeautomation/google-muell.nix>
# <stockholm/makefu/2configs/hw/pseyecam.nix>
# configure your hw:
# <stockholm/makefu/2configs/save-diskspace.nix>
# directly use the alsa device instead of attaching to pulse
<stockholm/makefu/2configs/audio/respeaker.nix>
<stockholm/makefu/2configs/home/rhasspy/default.nix>
<stockholm/makefu/2configs/home/rhasspy/led-control.nix>
];
krebs = {
enable = true;
@ -28,5 +35,4 @@ in {
documentation.info.enable = false;
documentation.man.enable = false;
documentation.nixos.enable = false;
sound.enable = false;
}

View File

@ -10,5 +10,6 @@
options = [ "noatime" ];
};
};
#hardware.raspberry-pi."4".fkms-3d.enable = true;
hardware.raspberry-pi."4".fkms-3d.enable = true;
hardware.raspberry-pi."4".audio.enable = true;
}

View File

@ -9,6 +9,12 @@ in {
imports = [
<stockholm/makefu>
./hetznercloud
{
# wait for mount
systemd.services.rtorrent.wantedBy = lib.mkForce [];
systemd.services.phpfpm-nextcloud.wantedBy = lib.mkForce [];
systemd.services.samba-smbd.wantedBy = lib.mkForce [];
}
{
users.users.lass = {
uid = 19002;
@ -103,6 +109,7 @@ in {
# <stockholm/makefu/2configs/sabnzbd.nix>
# <stockholm/makefu/2configs/mail/mail.euer.nix>
{ krebs.exim.enable = mkDefault true; }
<stockholm/makefu/2configs/nix-community/mediawiki-matrix-bot.nix>
# sharing
<stockholm/makefu/2configs/share/gum.nix> # samba sahre
@ -125,7 +132,7 @@ in {
<stockholm/makefu/2configs/backup/server.nix>
<stockholm/makefu/2configs/backup/state.nix>
<stockholm/makefu/2configs/wireguard/server.nix>
# <stockholm/makefu/2configs/wireguard/wiregrill.nix>
<stockholm/makefu/2configs/wireguard/wiregrill.nix>
{ # recent changes mediawiki bot
networking.firewall.allowedUDPPorts = [ 5005 5006 ];
@ -139,6 +146,7 @@ in {
<stockholm/makefu/2configs/deployment/rss/rss.euer.krebsco.de.nix> # postgres backend
<stockholm/makefu/2configs/deployment/rss/ratt.nix>
<stockholm/makefu/2configs/deployment/ntfysh.nix>
<stockholm/makefu/2configs/deployment/owncloud.nix> #postgres backend
### Moving owncloud data dir to /media/cloud/nextcloud-data
{
@ -173,7 +181,7 @@ in {
# <stockholm/makefu/2configs/nginx/iso.euer.nix>
# <stockholm/makefu/2configs/deployment/photostore.krebsco.de.nix>
<stockholm/makefu/2configs/deployment/graphs.nix>
# <stockholm/makefu/2configs/deployment/graphs.nix>
#<stockholm/makefu/2configs/deployment/owncloud.nix>
# <stockholm/makefu/2configs/deployment/board.euer.krebsco.de.nix>
#<stockholm/makefu/2configs/deployment/feed.euer.krebsco.de>
@ -184,7 +192,7 @@ in {
<stockholm/makefu/2configs/bgt/etherpad.euer.krebsco.de.nix>
# <stockholm/makefu/2configs/deployment/systemdultras-rss.nix>
# <stockholm/makefu/2configs/shiori.nix>
<stockholm/makefu/2configs/shiori.nix>
#<stockholm/makefu/2configs/workadventure>
<stockholm/makefu/2configs/bgt/download.binaergewitter.de.nix>

View File

@ -3,7 +3,7 @@ let
external-mac = "96:00:01:24:33:f4";
external-gw = "172.31.1.1";
external-ip = "142.132.189.140";
external-ip6 = "2a01:4f8:1c17:5cdf::2/64";
external-ip6 = "2a01:4f8:1c17:5cdf::2";
external-gw6 = "fe80::1";
external-netmask = 32;
external-netmask6 = 64;
@ -16,19 +16,20 @@ in
SUBSYSTEM=="net", ATTR{address}=="${external-mac}", NAME="${ext-if}"
'';
networking = {
enableIPv6 = true;
nat.enableIPv6 = true;
interfaces."${ext-if}" = {
useDHCP = true;
ipv6.addresses = [{
address = external-ip6;
prefixLength = external-netmask6;
}];
};
#ipv4.addresses = [{
# address = external-ip;
# prefixLength = external-netmask;
#}];
#ipv6.addresses = [{
# address = external-ip6;
# prefixLength = external-netmask6;
# }];
#};
#defaultGateway6 = { address = external-gw6; interface = ext-if; };
defaultGateway6 = { address = external-gw6; interface = ext-if; };
#defaultGateway = external-gw;
nameservers = [ "1.1.1.1" ];
};

View File

@ -32,8 +32,6 @@ in {
<stockholm/makefu/2configs/share>
# <stockholm/makefu/2configs/share/hetzner-client.nix>
# Services:
<stockholm/makefu/2configs/nix-community/mediawiki-matrix-bot.nix>
# torrent is managed by gum
# <stockholm/makefu/2configs/torrent/rtorrent.nix>

View File

@ -0,0 +1,27 @@
{ config,nixpkgsPath, pkgs, lib, ... }:
{
krebs = {
enable = true;
dns.providers.lan = "hosts";
build.user = config.krebs.users.makefu;
};
imports = [
(nixpkgsPath + "/nixos/modules/profiles/minimal.nix")
(nixpkgsPath + "/nixos/modules/profiles/installation-device.nix")
];
# cifs-utils fails to cross-compile
# Let's simplify this by removing all unneeded filesystems from the image.
boot.supportedFilesystems = lib.mkForce [ "vfat" ];
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
users.users = {
root = {
openssh.authorizedKeys.keys = [ config.krebs.users.makefu.pubkey ];
};
};
services.openssh.enable = true;
}

View File

@ -54,17 +54,19 @@ in {
<stockholm/makefu/2configs/share/omo.nix>
<stockholm/makefu/2configs/share/gum-client.nix>
<stockholm/makefu/2configs/sync>
<stockholm/makefu/2configs/dcpp/airdcpp.nix>
{ krebs.airdcpp.dcpp.shares = let
d = path: "/media/cryptX/${path}";
in {
emu.path = d "emu";
audiobooks.path = lib.mkForce (d "audiobooks");
incoming.path = lib.mkForce (d "torrent");
anime.path = d "anime";
};
krebs.airdcpp.dcpp.DownloadDirectory = "/media/cryptX/torrent/dcpp";
}
<stockholm/makefu/2configs/wireguard/wiregrill.nix>
#<stockholm/makefu/2configs/dcpp/airdcpp.nix>
#{ krebs.airdcpp.dcpp.shares = let
# d = path: "/media/cryptX/${path}";
# in {
# emu.path = d "emu";
# audiobooks.path = lib.mkForce (d "audiobooks");
# incoming.path = lib.mkForce (d "torrent");
# anime.path = d "anime";
# };
# krebs.airdcpp.dcpp.DownloadDirectory = "/media/cryptX/torrent/dcpp";
#}
{
# copy config from <secrets/sabnzbd.ini> to /var/lib/sabnzbd/
#services.sabnzbd.enable = true;
@ -84,12 +86,12 @@ in {
<stockholm/makefu/2configs/stats/telegraf>
# <stockholm/makefu/2configs/stats/telegraf/europastats.nix>
<stockholm/makefu/2configs/stats/telegraf/hamstats.nix>
# <stockholm/makefu/2configs/stats/arafetch.nix>
<stockholm/makefu/2configs/hw/cdrip.nix>
# services
{
services.nginx.enable = true;
networking.firewall.allowedTCPPorts = [ 80 ];
networking.firewall.allowedTCPPorts = [ 80 8123 ];
}
# <stockholm/makefu/2configs/syncthing.nix>
<stockholm/makefu/2configs/remote-build/slave.nix>
@ -100,10 +102,11 @@ in {
<stockholm/makefu/2configs/home/jellyfin.nix>
<stockholm/makefu/2configs/home/music.nix>
<stockholm/makefu/2configs/home/photoprism.nix>
<stockholm/makefu/2configs/home/tonie.nix>
# <stockholm/makefu/2configs/home/tonie.nix>
<stockholm/makefu/2configs/home/ps4srv.nix>
# <stockholm/makefu/2configs/home/metube.nix>
<stockholm/makefu/2configs/home/ham>
# <stockholm/makefu/2configs/home/ham>
<stockholm/makefu/2configs/home/ham/docker.nix>
<stockholm/makefu/2configs/home/zigbee2mqtt>
{
makefu.ps3netsrv = {

View File

@ -10,7 +10,7 @@ in {
<stockholm/makefu/2configs/binary-cache/nixos.nix>
<stockholm/makefu/2configs/home/rhasspy>
<stockholm/makefu/2configs/home/rhasspy/led-control.nix>
# <stockholm/makefu/2configs/hw/pseyecam.nix>
];
krebs = {
enable = true;

View File

@ -2,6 +2,8 @@
{
imports = [
<nixpkgs/nixos/modules/installer/scan/not-detected.nix>
./wifi.nix
./sound.nix
];
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
@ -18,4 +20,5 @@
boot.kernelParams = [ "net.ifnames=0" ];
networking.hostId = "0123AABB";
}

View File

@ -0,0 +1,51 @@
{ lib, ... }: {
imports = [
<stockholm/makefu/2configs/gui/snake-kiosk.nix>
];
nixpkgs.config.allowUnfree = true;
networking.networkmanager.enable = lib.mkForce false;
# sound.enable = true;
#hardware.pulseaudio = {
# enable = true;
# systemWide = true;
# tcp = {
# enable = true;
# anonymousClients.allowAll = true;
# };
#};
#users.users.makefu = {
# extraGroups = [ "pipewire" "audio" ];
#};
#services.xserver = {
# enable = true;
# # desktopManager.xterm.enable = true;
# desktopManager.xfce = {
# enable = true;
# noDesktop = true;
# };
# displayManager.autoLogin = {
# enable = true;
# user = "makefu";
# };
#};
hardware.pulseaudio.enable = lib.mkForce false;
security.rtkit.enable = true;
#services.pipewire = {
# enable = true;
# systemWide = true;
# socketActivation = false;
# alsa.enable = true;
# alsa.support32Bit = true;
# pulse.enable = true;
# config.pipewire-pulse = {
# "pulse.properties"."server.address" = [ "unix:native" "tcp:4713" ];
# };
#};
}

View File

@ -3,5 +3,4 @@
full = true;
home-manager = true;
hw = true;
disko = true;
}

View File

@ -0,0 +1,6 @@
{
networking.wireless = {
enable = true;
networks = import <secrets/wifi.nix>;
};
}

View File

@ -22,7 +22,7 @@ in {
# <stockholm/makefu/2configs/virtualisation/virtualbox.nix>
<stockholm/makefu/2configs/tinc/retiolum.nix>
<stockholm/makefu/2configs/gui/wbob-kiosk.nix>
{ environment.systemPackages = [ pkgs.nano ]; }
{ environment.systemPackages = [ pkgs.brother_ql_web pkgs.nano ]; }
# <stockholm/makefu/2configs/gui/studio-virtual.nix>
# <stockholm/makefu/2configs/audio/jack-on-pulse.nix>
@ -53,6 +53,7 @@ in {
<stockholm/makefu/2configs/bureautomation> # new hass entry point
<stockholm/makefu/2configs/bureautomation/led-fader.nix>
<stockholm/makefu/2configs/bureautomation/printer.nix>
# <stockholm/makefu/2configs/bureautomation/kalauerbot.nix> now runs in thales
# <stockholm/makefu/2configs/bureautomation/visitor-photostore.nix>
# <stockholm/makefu/2configs/bureautomation/mpd.nix> #mpd is only used for TTS, this is the web interface
@ -100,7 +101,9 @@ in {
<stockholm/makefu/2configs/backup/state.nix>
# temporary
# <stockholm/makefu/2configs/temp/rst-issue.nix>
{ services.jellyfin.enable = true; }
{
services.jellyfin.enable = true;
}
];
krebs = {

View File

@ -48,6 +48,16 @@
{ bits = 4096; path = (toString <secrets/ssh_host_rsa_key>); type = "rsa";}
];
}
#{
# imports = [
# <stockholm/makefu/2configs/bureautomation/rhasspy.nix>
# ];
# services.pipewire.config.pipewire-pulse = {
# "pulse.properties"."server.address" = [ "unix:native" "tcp:4713" ];
# };
# networking.firewall.allowedTCPPorts = [ 4713 ];
#}
#{
# users.users.makefu.packages = with pkgs;[ mpc_cli ncmpcpp ];
@ -130,7 +140,7 @@
# <stockholm/makefu/2configs/deployment/hound>
# <stockholm/makefu/2configs/deployment/photostore.krebsco.de.nix>
# <stockholm/makefu/2configs/deployment/bureautomation/hass.nix>
<stockholm/makefu/2configs/bureautomation/office-radio>
# <stockholm/makefu/2configs/bureautomation/office-radio>
# Krebs
<stockholm/makefu/2configs/tinc/retiolum.nix>
@ -146,7 +156,7 @@
<stockholm/makefu/2configs/mail-client.nix>
<stockholm/makefu/2configs/printer.nix>
# <stockholm/makefu/2configs/syncthing.nix>
<stockholm/makefu/2configs/sync>
# <stockholm/makefu/2configs/sync>
# Virtualization
# <stockholm/makefu/2configs/virtualisation/libvirt.nix>
@ -179,6 +189,7 @@
# temporary
# { services.redis.enable = true; }
# citadel exporter
# { services.mongodb.enable = true; }
# { services.elasticsearch.enable = true; }
# <stockholm/makefu/2configs/deployment/nixos.wiki>
@ -189,27 +200,28 @@
# <stockholm/makefu/2configs/lanparty/lancache-dns.nix>
# <stockholm/makefu/2configs/lanparty/samba.nix>
# <stockholm/makefu/2configs/lanparty/mumble-server.nix>
<stockholm/makefu/2configs/wireguard/wiregrill.nix>
{
networking.wireguard.interfaces.wg0 = {
ips = [ "10.244.0.2/24" ];
privateKeyFile = (toString <secrets>) + "/wireguard.key";
allowedIPsAsRoutes = true;
peers = [
{
# gum
endpoint = "${config.krebs.hosts.gum.nets.internet.ip4.addr}:51820";
allowedIPs = [ "10.244.0.0/24" ];
publicKey = "yAKvxTvcEVdn+MeKsmptZkR3XSEue+wSyLxwcjBYxxo=";
}
#{
# # vbob
# allowedIPs = [ "10.244.0.3/32" ];
# publicKey = "Lju7EsCu1OWXhkhdNR7c/uiN60nr0TUPHQ+s8ULPQTw=";
#}
];
};
}
# {
# networking.wireguard.interfaces.wg0 = {
# ips = [ "10.244.0.2/24" ];
# privateKeyFile = (toString <secrets>) + "/wireguard.key";
# allowedIPsAsRoutes = true;
# peers = [
# {
# # gum
# endpoint = "${config.krebs.hosts.gum.nets.internet.ip4.addr}:51820";
# allowedIPs = [ "10.244.0.0/24" ];
# publicKey = "yAKvxTvcEVdn+MeKsmptZkR3XSEue+wSyLxwcjBYxxo=";
# }
# #{
# # # vbob
# # allowedIPs = [ "10.244.0.3/32" ];
# # publicKey = "Lju7EsCu1OWXhkhdNR7c/uiN60nr0TUPHQ+s8ULPQTw=";
# #}
# ];
# };
# }
];

View File

@ -0,0 +1,6 @@
{ pkgs, ... }:
{
powerManagement.powertop.enable = true;
services.power-profiles-daemon.enable = true;
users.users.makefu.packages = [ pkgs.gnome.gnome-power-manager ];
}

View File

@ -4,6 +4,7 @@
imports = [
./zfs.nix
./input.nix
./battery.nix
<stockholm/makefu/2configs/hw/bluetooth.nix>
<nixos-hardware/lenovo/thinkpad/l14/amd> # close enough
# <stockholm/makefu/2configs/hw/tpm.nix>
@ -17,23 +18,26 @@
# services.xserver.enable = lib.mkForce false;
services.xserver.videoDrivers = [
"amdgpu"
services.xserver.videoDrivers = [ "amdgpu" ];
boot.initrd.kernelModules = [ "amdgpu" ];
hardware.opengl.driSupport = true;
hardware.opengl.extraPackages = [ pkgs.amdvlk pkgs.rocm-opencl-icd pkgs.rocm-opencl-runtime ];
# For 32 bit applications
hardware.opengl.driSupport32Bit = true;
hardware.opengl.extraPackages32 = with pkgs; [
driversi686Linux.amdvlk
];
hardware.opengl.extraPackages = [ pkgs.amdvlk pkgs.rocm-opencl-icd ];
# is required for amd graphics support ( xorg wont boot otherwise )
#boot.kernelPackages = pkgs.linuxPackages_latest;
boot.kernelPackages = lib.mkForce pkgs.linuxPackages;
environment.variables.VK_ICD_FILENAMES =
"/run/opengl-driver/share/vulkan/icd.d/amd_icd64.json";
services.fwupd.enable = true;
programs.light.enable = true;
users.groups.video = {};
users.users.makefu.extraGroups = [ "video" ];
users.groups.render = {};
users.users.makefu.extraGroups = [ "video" "render" ];
boot.extraModprobeConfig = ''
options thinkpad_acpi fan_control=1

View File

@ -0,0 +1,67 @@
{ disk ? "/dev/sda", ... }: {
disko.devices = {
disk = {
nvme = {
type = "disk";
device = disk;
content = {
type = "table";
format = "gpt";
partitions = [
{
name = "ESP";
start = "0";
end = "512MiB";
fs-type = "fat32";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
}
{
name = "zfs";
start = "512MiB";
end = "100%";
content = {
type = "zfs";
pool = "tank";
};
}
];
};
};
};
zpool = {
tank = {
type = "zpool";
rootFsOptions = {
compression = "lz4";
#reservation = "5G";
"com.sun:auto-snapshot" = "false";
};
mountpoint = null;
postCreateHook = "zfs snapshot tank@blank";
datasets = {
root = {
type = "zfs_fs";
mountpoint = "/";
options = {
encryption = "aes-256-gcm";
keyformat = "passphrase";
"com.sun:auto-snapshot" = "true";
};
#keylocation = "file:///tmp/secret.key";
};
"root/home" = {
type = "zfs_fs";
mountpoint = "/home";
};
};
};
};
};
}

View File

@ -4,14 +4,16 @@
# 1. for pressing insert hold shift+fn+Fin
# scroll by holding middle mouse
services.xserver.displayManager.sessionCommands =''
xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation" 8 1
xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Button" 8 2
xinput set-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Axes" 6 7 4 5
# configure timeout of pressing and holding middle button
# xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Timeout" 8 200
xinput disable 'ETPS/2 Elantech Touchpad'
'';
#services.xserver.displayManager.sessionCommands =''
# xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation" 8 1
# xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Button" 8 2
# xinput set-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Axes" 6 7 4 5
# # configure timeout of pressing and holding middle button
# # xinput set-int-prop "ETPS/2 Elantech TrackPoint" "Evdev Wheel Emulation Timeout" 8 200
# xinput disable 'ETPS/2 Elantech Touchpad'
#'';
services.xserver.libinput.enable = true;
boot.kernelParams = [
#"psmouse.proto=imps"
#"psmouse.proto=bare"
@ -27,20 +29,20 @@
{ keys = [ 224 ]; events = [ "key" ]; command = "${pkgs.light}/bin/light -U 10"; } # fn - F6
# fn - 4 => suspend
# fn - d => lcdshadow
{ keys = [ 227 ]; events = [ "key" ]; command = builtins.toString ( # fn - F7
pkgs.writers.writeDash "toggle_touchpad" ''
PATH=${lib.makeBinPath [ pkgs.xorg.xinput pkgs.gnugrep ]}
DISPLAY=:0
export DISPLAY PATH
#{ keys = [ 227 ]; events = [ "key" ]; command = builtins.toString ( # fn - F7
# pkgs.writers.writeDash "toggle_touchpad" ''
# PATH=${lib.makeBinPath [ pkgs.xorg.xinput pkgs.gnugrep ]}
# DISPLAY=:0
# export DISPLAY PATH
device=$(xinput list --name-only | grep Touchpad)
if [ "$(xinput list-props "$device" | grep -P ".*Device Enabled.*\K.(?=$)" -o)" -eq 1 ];then
xinput disable "$device"
else
xinput enable "$device"
fi
'');
}
# device=$(xinput list --name-only | grep Touchpad)
# if [ "$(xinput list-props "$device" | grep -P ".*Device Enabled.*\K.(?=$)" -o)" -eq 1 ];then
# xinput disable "$device"
# else
# xinput enable "$device"
# fi
# '');
#}
];
};
}

View File

@ -1,52 +0,0 @@
{ config, pkgs, ... }:
let
pulse = pkgs.pulseaudioFull;
user = config.makefu.gui.user;
wait_time = 30;
in
{
sound.enable = true;
hardware.pulseaudio = {
enable = true;
package = pulse;
};
environment.systemPackages = with pkgs; [
jack2Full
jack_capture
];
# from http://anderspapitto.com/posts/2015-11-26-overtone-on-nixos-with-jack-and-pulseaudio.html
systemd.user.services = {
jackdbus = {
description = "Runs jack, and points pulseaudio at it";
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeScript "start_jack.sh" ''
#! ${pkgs.bash}/bin/bash
. ${config.system.build.setEnvironment}
# TODO: correctly wait for pulseaudio, cannot use pulseaudio.service
sleep ${toString wait_time} # wait for the gui to load
${pkgs.jack2Full}/bin/jack_control start
sleep 3 # give some time for sources/sinks to be created
${pulse}/bin/pacmd set-default-sink jack_out
${pulse}/bin/pacmd set-default-source jack_in
'';
ExecStop = pkgs.writeScript "stop_jack.sh" ''
#! ${pkgs.bash}/bin/bash
. ${config.system.build.setEnvironment}
${pkgs.jack2Full}/bin/jack_control stop
'';
RemainAfterExit = true;
Restart = "always";
RestartSec = "5";
};
after = [ "display-manager.service" "sound.target" ];
wantedBy = [ "default.target" ];
};
};
}

View File

@ -0,0 +1,122 @@
{ config, lib, pkgs, ... }:
let
seeed-voicecard = (pkgs.callPackage ../../5pkgs/seeed-voicecard { kernel = config.boot.kernelPackages.kernel; });
in
{
hardware.raspberry-pi."4".i2c1.enable = true;
hardware.raspberry-pi."4".audio.enable = true;
hardware.raspberry-pi."4".apply-overlays-dtmerge.enable = true;
hardware.deviceTree.filter = lib.mkForce "bcm2711-rpi-4-b.dtb";
security.rtkit.enable = true;
environment.systemPackages = with pkgs; [
alsaUtils
i2c-tools
ponymix
];
sound.enable = true;
hardware.pulseaudio.enable = lib.mkForce false;
services.pipewire = {
enable = true;
systemWide = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
services.pipewire.config.pipewire-pulse = {
"pulse.properties"."server.address" = [ "unix:native" "tcp:4713" ];
};
sound.extraConfig = ''
pcm.!default {
type asym
playback.pcm "playback"
capture.pcm "ac108"
}
pcm.ac108 {
type plug
slave.pcm "hw:seeed4micvoicec"
}
'' ;
boot.extraModulePackages = [
seeed-voicecard
];
boot.initrd.kernelModules = [
"snd-soc-seeed-voicecard"
"snd-soc-ac108"
"i2c-dev"
#"i2c-bcm2708"
#"snd-soc-wm8960"
];
boot.loader.raspberryPi.firmwareConfig = [
"dtparam=i2c_arm=on"
"dtparam=i2s=on"
"dtparam=spi=on"
"dtparam=i2c1=on"
# dtoverlay=seeeed-8mic-voicecard not required because we use hardware.deviceTree
];
hardware.deviceTree = {
enable = true;
overlays = [
{ name = "respeaker-4mic"; dtsFile = "${seeed-voicecard}/lib/dts/seeed-4mic-voicecard-overlay.dts";}
{ name = "spi"; dtsText = ''
/dts-v1/;
/plugin/;
/ {
compatible = "raspberrypi";
fragment@0 {
target = <&spi>;
__overlay__ {
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
#address-cells = <1>;
#size-cells = <0>;
spidev@0 {
reg = <0>; // CE0
spi-max-frequency = <500000>;
compatible = "spidev";
};
spidev@1 {
reg = <1>; // CE1
spi-max-frequency = <500000>;
compatible = "spidev";
};
};
};
fragment@1 {
target = <&alt0>;
__overlay__ {
// Drop GPIO 7, SPI 8-11
brcm,pins = <4 5>;
};
};
fragment@2 {
target = <&gpio>;
__overlay__ {
spi0_pins: spi0_pins {
brcm,pins = <9 10 11>;
brcm,function = <4>; // alt0
};
spi0_cs_pins: spi0_cs_pins {
brcm,pins = <8 7>;
brcm,function = <1>; // out
};
};
};
};
'';}
];
};
}

View File

@ -2,7 +2,7 @@
0. Sendung twittern und mastodieren (eine Woche + eine Stunde vorher) von Ingo/l33tname (wichtig)
1. `eine` Person anrufen (den Host):
- markus 162dcbf89f@studio.link
- markus madmas@studio.link
- Felix1 makefu@studio.link
- L33tFelix l33tname@studio.link
- Ingo ingo@studio.link

View File

@ -3,6 +3,7 @@
services.bitlbee = {
enable = true;
# libpurple_plugins = [ pkgs.telegram-purple pkgs.pidgin-skypeweb];
plugins = [ pkgs.bitlbee-mastodon ];
};
users.users.makefu.packages = with pkgs; [ weechat tmux ];
state = [ "/var/lib/bitlbee" ];

View File

@ -0,0 +1,23 @@
{pkgs, ... }:
let
pkg = pkgs.brother_ql_web;
in {
systemd.services.brother-ql-web = {
after = [ "network.target" ];
description = "Brother QL Web Interface";
wantedBy = [ "multi-user.target" ];
environment = {
FLASK_PRINTER = "usb://0x04f9:0x209b/000F1Z401759";
FLASK_MODEL = "QL-800";
#FLASK_SERVER_PORT = "8013";
#FLASK_LABEL_DEFAULT_SIZE = "d24";
#FLASK_LABEL_DEFAULT_QR_SIZE = "7";
};
serviceConfig = {
ExecStart = "${pkg}/bin/brother_ql_web";
DynamicUser = true;
SupplementaryGroups = "lp";
Restart = "always";
};
};
}

View File

@ -0,0 +1,28 @@
{ pkgs, config, ... }:
let
mainUser = config.krebs.build.user.name;
in {
imports = [
./brother-ql-web.nix
];
services.printing = {
enable = true;
drivers = with pkgs;[
brlaser
cups-ptouch
];
};
users.users.kiosk.extraGroups = [ "scanner" "lp" ];
state = [ "/var/lib/cups"];
users.users.kiosk.packages = with pkgs;[
python3Packages.brother-ql
libreoffice
qrencode
imagemagick
];
services.udev.extraRules = ''
SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f9", ATTRS{idProduct}=="209b", ATTRS{serial}=="000F1Z401759", MODE="0664", GROUP="lp", SYMLINK+="usb/lp0"
'';
}

View File

@ -31,6 +31,7 @@ with import <stockholm/lib>;
};
};
nix.settings.trusted-users = [ config.krebs.build.user.name ];
nix.settings.experimental-features = [ "flakes" "nix-command" ];
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages;

View File

@ -26,18 +26,6 @@
zipcode: 70378
q: Werkbank
distance: 5
- name: Stirnthermometer
zipcode: 70378
q: Stirnthermometer
distance: 5
- name: Ohrthermometer
zipcode: 70378
q: Ohrthermometer
distance: 5
- name: Fieberthermometer
zipcode: 70378
q: Fieberthermometer
distance: 5
- name: Einhell
zipcode: 70378
q: Einhell

View File

@ -0,0 +1,9 @@
{ config, pkgs, ... }:
{
imports =
[ ./mediawiki.nix
./network.nix
];
}

View File

@ -0,0 +1,481 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types;
cfg = config.services.mediawiki;
fpm = config.services.phpfpm.pools.mediawiki;
user = "mediawiki";
group = config.services.httpd.group;
cacheDir = "/var/cache/mediawiki";
stateDir = "/var/lib/mediawiki";
pkg = pkgs.stdenv.mkDerivation rec {
pname = "mediawiki-full";
version = src.version;
src = cfg.package;
installPhase = ''
mkdir -p $out
cp -r * $out/
rm -rf $out/share/mediawiki/skins/*
rm -rf $out/share/mediawiki/extensions/*
${concatStringsSep "\n" (mapAttrsToList (k: v: ''
ln -s ${v} $out/share/mediawiki/skins/${k}
'') cfg.skins)}
${concatStringsSep "\n" (mapAttrsToList (k: v: ''
ln -s ${if v != null then v else "$src/share/mediawiki/extensions/${k}"} $out/share/mediawiki/extensions/${k}
'') cfg.extensions)}
'';
};
mediawikiScripts = pkgs.runCommand "mediawiki-scripts" {
buildInputs = [ pkgs.makeWrapper ];
preferLocalBuild = true;
} ''
mkdir -p $out/bin
for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
--set MEDIAWIKI_CONFIG ${mediawikiConfig} \
--add-flags ${pkg}/share/mediawiki/maintenance/$i
done
'';
mediawikiConfig = pkgs.writeText "LocalSettings.php" ''
<?php
# Protect against web entry
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$wgSitename = "${cfg.name}";
$wgMetaNamespace = false;
## The URL base path to the directory containing the wiki;
## defaults for all runtime URL paths are based off of this.
## For more information on customizing the URLs
## (like /w/index.php/Page_title to /wiki/Page_title) please see:
## https://www.mediawiki.org/wiki/Manual:Short_URL
$wgScriptPath = "${cfg.basePath}";
## The protocol and server name to use in fully-qualified URLs
#$wgServer = "${if cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL then "https" else "http"}://${cfg.virtualHost.hostName}";
#$wgServer = "";
$wgServer = "http://localhost";
## The URL path to static resources (images, scripts, etc.)
$wgResourceBasePath = $wgScriptPath;
## The URL path to the logo. Make sure you change this from the default,
## or else you'll overwrite your logo when you upgrade!
$wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
## UPO means: this is also a user preference option
$wgEnableEmail = true;
$wgEnableUserEmail = true; # UPO
$wgEmergencyContact = "${if cfg.virtualHost.adminAddr != null then cfg.virtualHost.adminAddr else config.services.httpd.adminAddr}";
$wgPasswordSender = $wgEmergencyContact;
$wgEnotifUserTalk = false; # UPO
$wgEnotifWatchlist = false; # UPO
$wgEmailAuthentication = true;
## Database settings
$wgDBtype = "${cfg.database.type}";
$wgDBserver = "${cfg.database.host}:${if cfg.database.socket != null then cfg.database.socket else toString cfg.database.port}";
$wgDBname = "${cfg.database.name}";
$wgDBuser = "${cfg.database.user}";
${optionalString (cfg.database.passwordFile != null) "$wgDBpassword = file_get_contents(\"${cfg.database.passwordFile}\");"}
${optionalString (cfg.database.type == "mysql" && cfg.database.tablePrefix != null) ''
# MySQL specific settings
$wgDBprefix = "${cfg.database.tablePrefix}";
''}
${optionalString (cfg.database.type == "mysql") ''
# MySQL table options to use during installation or update
$wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
''}
## Shared memory settings
$wgMainCacheType = CACHE_NONE;
$wgMemCachedServers = [];
${optionalString (cfg.uploadsDir != null) ''
$wgEnableUploads = true;
$wgUploadDirectory = "${cfg.uploadsDir}";
''}
$wgUseImageMagick = true;
$wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
# InstantCommons allows wiki to use images from https://commons.wikimedia.org
$wgUseInstantCommons = false;
# Periodically send a pingback to https://www.mediawiki.org/ with basic data
# about this MediaWiki instance. The Wikimedia Foundation shares this data
# with MediaWiki developers to help guide future development efforts.
$wgPingback = true;
## If you use ImageMagick (or any other shell command) on a
## Linux server, this will need to be set to the name of an
## available UTF-8 locale
$wgShellLocale = "C.UTF-8";
## Set $wgCacheDirectory to a writable directory on the web server
## to make your wiki go slightly faster. The directory should not
## be publically accessible from the web.
$wgCacheDirectory = "${cacheDir}";
# Site language code, should be one of the list in ./languages/data/Names.php
$wgLanguageCode = "en";
$wgSecretKey = file_get_contents("${stateDir}/secret.key");
# Changing this will log out all existing sessions.
$wgAuthenticationTokenVersion = "";
## For attaching licensing metadata to pages, and displaying an
## appropriate copyright notice / icon. GNU Free Documentation
## License and Creative Commons licenses are supported so far.
$wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
$wgRightsUrl = "";
$wgRightsText = "";
$wgRightsIcon = "";
# Path to the GNU diff3 utility. Used for conflict resolution.
$wgDiff = "${pkgs.diffutils}/bin/diff";
$wgDiff3 = "${pkgs.diffutils}/bin/diff3";
# Enabled skins.
${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadSkin('${k}');") cfg.skins)}
# Enabled extensions.
${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadExtension('${k}');") cfg.extensions)}
# End of automatically generated settings.
# Add more configuration options below.
${cfg.extraConfig}
'';
in
{
# interface
options = {
services.mediawiki = {
enable = mkEnableOption "MediaWiki";
package = mkOption {
type = types.package;
default = pkgs.mediawiki;
description = "Which MediaWiki package to use.";
};
basePath = mkOption {
type = types.str;
default = "/";
description = "Base path to Wiki";
};
name = mkOption {
default = "MediaWiki";
example = "Foobar Wiki";
description = "Name of the wiki.";
};
uploadsDir = mkOption {
type = types.nullOr types.path;
default = "${stateDir}/uploads";
description = ''
This directory is used for uploads of pictures. The directory passed here is automatically
created and permissions adjusted as required.
'';
};
passwordFile = mkOption {
type = types.path;
description = "A file containing the initial password for the admin user.";
example = "/run/keys/mediawiki-password";
};
skins = mkOption {
default = {};
type = types.attrsOf types.path;
description = ''
Attribute set of paths whose content is copied to the <filename>skins</filename>
subdirectory of the MediaWiki installation in addition to the default skins.
'';
};
extensions = mkOption {
default = {};
type = types.attrsOf (types.nullOr types.path);
description = ''
Attribute set of paths whose content is copied to the <filename>extensions</filename>
subdirectory of the MediaWiki installation and enabled in configuration.
Use <literal>null</literal> instead of path to enable extensions that are part of MediaWiki.
'';
example = literalExample ''
{
Matomo = pkgs.fetchzip {
url = "https://github.com/DaSchTour/matomo-mediawiki-extension/archive/v4.0.1.tar.gz";
sha256 = "0g5rd3zp0avwlmqagc59cg9bbkn3r7wx7p6yr80s644mj6dlvs1b";
};
ParserFunctions = null;
}
'';
};
database = {
type = mkOption {
type = types.enum [ "mysql" "postgres" "sqlite" "mssql" "oracle" ];
default = "mysql";
description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
};
host = mkOption {
type = types.str;
default = "localhost";
description = "Database host address.";
};
port = mkOption {
type = types.port;
default = 3306;
description = "Database host port.";
};
name = mkOption {
type = types.str;
default = "mediawiki";
description = "Database name.";
};
user = mkOption {
type = types.str;
default = "mediawiki";
description = "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/mediawiki-dbpassword";
description = ''
A file containing the password corresponding to
<option>database.user</option>.
'';
};
tablePrefix = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
If you only have access to a single database and wish to install more than
one version of MediaWiki, or have other applications that also use the
database, you can give the table names a unique prefix to stop any naming
conflicts or confusion.
See <link xlink:href='https://www.mediawiki.org/wiki/Manual:$wgDBprefix'/>.
'';
};
socket = mkOption {
type = types.nullOr types.path;
default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null;
defaultText = "/run/mysqld/mysqld.sock";
description = "Path to the unix socket file to use for authentication.";
};
createLocally = mkOption {
type = types.bool;
default = cfg.database.type == "mysql";
defaultText = "true";
description = ''
Create the database and database user locally.
This currently only applies if database type "mysql" is selected.
'';
};
};
virtualHost = mkOption {
type = types.submodule (import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix>);
example = literalExample ''
{
hostName = "mediawiki.example.org";
adminAddr = "webmaster@example.org";
forceSSL = true;
enableACME = true;
}
'';
description = ''
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
'';
};
poolConfig = mkOption {
type = with types; attrsOf (oneOf [ str int bool ]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = ''
Options for the MediaWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal>
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.lines;
description = ''
Any additional text to be appended to MediaWiki's
LocalSettings.php configuration file. For configuration
settings, see <link xlink:href="https://www.mediawiki.org/wiki/Manual:Configuration_settings"/>.
'';
default = "";
example = ''
$wgEnableEmail = false;
'';
};
};
};
# implementation
config = mkIf cfg.enable {
assertions = [
{ assertion = cfg.database.createLocally -> cfg.database.type == "mysql";
message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'";
}
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true";
}
];
services.mediawiki.skins = {
MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook";
Timeless = "${cfg.package}/share/mediawiki/skins/Timeless";
Vector = "${cfg.package}/share/mediawiki/skins/Vector";
};
services.mysql = mkIf cfg.database.createLocally {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
services.phpfpm.pools.mediawiki = {
inherit user group;
phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}";
settings = {
"listen.owner" = config.services.httpd.user;
"listen.group" = config.services.httpd.group;
} // cfg.poolConfig;
};
services.httpd = {
enable = true;
extraModules = [ "proxy_fcgi" ];
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
documentRoot = mkForce "${pkg}/share/mediawiki";
extraConfig = ''
<Directory "${pkg}/share/mediawiki">
<FilesMatch "\.php$">
<If "-f %{REQUEST_FILENAME}">
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
</If>
</FilesMatch>
Require all granted
DirectoryIndex index.php
AllowOverride All
</Directory>
'' + optionalString (cfg.uploadsDir != null) ''
Alias "/images" "${cfg.uploadsDir}"
<Directory "${cfg.uploadsDir}">
Require all granted
</Directory>
'';
} ];
};
systemd.tmpfiles.rules = [
"d '${stateDir}' 0750 ${user} ${group} - -"
"d '${cacheDir}' 0750 ${user} ${group} - -"
] ++ optionals (cfg.uploadsDir != null) [
"d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
"Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
];
systemd.services.mediawiki-init = {
wantedBy = [ "multi-user.target" ];
before = [ "phpfpm-mediawiki.service" ];
after = optional cfg.database.createLocally "mysql.service";
script = ''
if ! test -e "${stateDir}/secret.key"; then
tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 64 > ${stateDir}/secret.key
fi
echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
--confpath /tmp \
--scriptpath ${cfg.basePath} \
--dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \
--dbport ${toString cfg.database.port} \
--dbname ${cfg.database.name} \
${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \
--dbuser ${cfg.database.user} \
${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \
--passfile ${cfg.passwordFile} \
"${cfg.name}" \
admin
${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
'';
serviceConfig = {
Type = "oneshot";
User = user;
Group = group;
PrivateTmp = true;
};
};
systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service";
users.users.${user} = {
group = group;
isSystemUser = true;
};
environment.systemPackages = [ mediawikiScripts ];
};
}

View File

@ -0,0 +1,67 @@
{ config, pkgs, ... }:
let
hostAddress = "192.168.48.1";
localAddress = "192.168.48.3";
in
{
containers.mediawiki =
{ autoStart = true;
privateNetwork = true;
inherit hostAddress localAddress;
config = { config, pkgs, ... }:
{
# NOTE: This disabling and importing is so that the basePath can be altered
disabledModules = [ "services/web-apps/mediawiki.nix" ];
imports = [
./mediawiki.module.nix
];
time.timeZone = "America/New_York";
system.stateVersion = "20.09";
networking.defaultGateway = hostAddress;
# NOTE: you might want to change this namserver address
networking.nameservers = [ "8.8.8.8" ];
networking.firewall.allowedTCPPorts = [ 80 ];
services.mediawiki = {
enable = true;
name = "Example Containerized Wiki";
# NOTE: here is where the basePath is specified, which requires the imported mediawiki NixOS module
basePath = "/wiki";
passwordFile = ./mediawiki.password.txt;
extraConfig = ''
$wgRCFeeds['euerkrebsco'] = array(
'formatter' => 'JSONRCFeedFormatter',
'uri' => 'udp://euer.krebsco.de:5005',
'add_interwiki_prefix' => false,
'omit_bots' => true,
);
$wgRCFeeds['euerkrebscoIRC'] = array(
'formatter' => 'IRCColourfulRCFeedFormatter',
'uri' => 'udp://euer.krebsco.de:5006',
'add_interwiki_prefix' => false,
'omit_bots' => true,
);
'';
virtualHost = {
hostName = "localhost";
adminAddr = "root@localhost";
forceSSL = false;
addSSL = false;
onlySSL = false;
enableACME = false;
};
};
};
};
# Put the MediaWiki web page behind an NGINX proxy
services.nginx = {
enable = true;
virtualHosts.localhost.locations."/wiki" = {
# NOTE: the slash at the end of the URI is important. It causes the location base path to be removed when passed onto the proxy
proxyPass = "http://${localAddress}:80/";
};
};
}

View File

@ -0,0 +1 @@
thisisthepassword

View File

@ -0,0 +1,6 @@
{
networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
networking.nat.enable = true;
networking.nat.internalInterfaces = ["ve-+"];
networking.nat.externalInterface = "wlan0";
}

View File

@ -0,0 +1,41 @@
{ lib, config, ... }:
let
web-port = 19455;
hostn = "ntfy.euer.krebsco.de";
internal-ip = config.krebs.build.host.nets.retiolum.ip4.addr;
in
{
services.ntfy-sh = {
enable = true;
settings = {
listen-http = "127.0.0.1:${toString web-port}";
auth-file = "/var/lib/ntfy-sh/user.db";
auth-default-access = "deny-all";
behind-proxy = true;
attachment-cache-dir = "/media/cloud/ntfy-sh/attachments";
attachment-file-size-limit = "500m";
attachment-total-size-limit = "100g";
base-url = "https://ntfy.euer.krebsco.de";
attachment-expiry-duration = "48h";
};
};
systemd.services.ntfy-sh.serviceConfig = {
StateDirectory = "ntfy-sh";
SupplementaryGroups = [ "download" ];
};
services.nginx = {
enable = lib.mkDefault true;
virtualHosts."${hostn}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString web-port}/";
proxyWebsockets = true;
recommendedProxySettings = true;
};
};
};
}

View File

@ -59,7 +59,7 @@ systemd.services.postgresqlBackup-nextcloud.serviceConfig.SupplementaryGroups =
users.users.nextcloud.extraGroups = [ "download" ];
services.nextcloud = {
enable = true;
package = pkgs.nextcloud24;
package = pkgs.nextcloud25;
hostName = "o.euer.krebsco.de";
# Use HTTPS for links
https = true;
@ -97,5 +97,11 @@ systemd.services.postgresqlBackup-nextcloud.serviceConfig.SupplementaryGroups =
systemd.services."nextcloud-setup" = {
requires = ["postgresql.service"];
after = ["postgresql.service"];
serviceConfig.RequiresMountFor = [ "/media/cloud" ];
};
systemd.services."phpfpm-nextcloud".serviceConfig.RequiresMountFor = [
"/media/cloud"
"/var/lib/nextcloud/data"
];
systemd.services."phpfpm".serviceConfig.RequiresMountFor = [ "/media/cloud" ];
}

View File

@ -16,6 +16,10 @@ in {
enable = true;
databases = [ config.services.tt-rss.database.name ];
};
systemd.services.tt-rss.serviceConfig = {
Restart = lib.mkForce "always";
};
systemd.services.postgresqlBackup-tt_rss.serviceConfig.SupplementaryGroups = [ "download" ];
services.nginx.virtualHosts."${fqdn}" = {

View File

@ -3,5 +3,7 @@ https://www.ebay-kleinanzeigen.de/s-stuttgart/zigbee/k0l9280
https://www.ebay-kleinanzeigen.de/s-70378/d%C3%B6rrautomat/k0l9334r5
https://www.ebay-kleinanzeigen.de/s-zu-verschenken/muehlhausen/c192l9313
https://www.ebay-kleinanzeigen.de/s-spielzeug/muehlhausen/brettspiel/k0c23l9313
https://www.ebay-kleinanzeigen.de/s-muehlhausen/labeldrucker/k0l9313r5
https://www.ebay-kleinanzeigen.de/s-muehlhausen/dymo/k0l9313r5
https://www.ebay-kleinanzeigen.de/s-zu-verschenken/muehlhausen/lautsprecher/k0c192l9313r5
https://www.ebay-kleinanzeigen.de/s-muehlhausen/preis::40/winkelschleifer/k0l9313r5
https://www.ebay-kleinanzeigen.de/s-muehlhausen/preis::40/kontaktgrill/k0l9313r5

View File

@ -12,7 +12,7 @@
#"UltiSnips"
# vim-nix handles indentation better but does not perform sanity
"vim-nix"
# "vim-addon-nix"
"vim-addon-nix"
"vim-better-whitespace"
];
};

View File

@ -49,7 +49,6 @@ set matchtime=3
set hlsearch
autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
hi MatchParen cterm=none ctermbg=green ctermfg=blue
let g:better_whitespace_enabled=1
let g:strip_whitespace_on_save=1
@ -114,3 +113,5 @@ let g:UltiSnipsExpandTrigger = "<c-j>"
let g:UltiSnipsJumpForwardTrigger = "<c-j>"
let g:UltiSnipsJumpBackwardTrigger = "<c-p>"
let g:UltiSnipsListSnippets = "<c-k>" "List possible snippets based on current file
hi MatchParen cterm=none ctermbg=green ctermfg=blue

View File

@ -18,30 +18,28 @@ in
imports = [
./urxvtd.nix
./pipewire.nix
./gnome.nix
];
# services.redshift.enable = true;
services.xserver = {
enable = true;
layout = "us";
xkbVariant = "altgr-intl";
xkbOptions = "ctrl:nocaps, eurosign:e";
windowManager = {
awesome.enable = true;
awesome.noArgb = true;
awesome.luaModules = [ pkgs.luaPackages.vicious ];
};
displayManager.defaultSession = lib.mkDefault "none+awesome";
displayManager.autoLogin = {
enable = true;
user = mainUser;
};
# windowManager = {
# awesome.enable = true;
# awesome.noArgb = true;
# awesome.luaModules = [ pkgs.luaPackages.vicious ];
# };
# displayManager.defaultSession = lib.mkDefault "none+awesome";
};
environment.systemPackages = [ pkgs.gnome.adwaita-icon-theme ];
# lid switch is handled via button presses
services.logind.lidSwitch = lib.mkDefault "ignore";
makefu.awesome.enable = true;
# services.logind.lidSwitch = lib.mkDefault "ignore";
#makefu.awesome.enable = true;
console.font = "Lat2-Terminus16";
fonts = {

View File

@ -0,0 +1,63 @@
{ config, lib, pkgs, ... }:
let
mainUser = config.krebs.build.user.name;
in
{
programs.gnome-terminal.enable = true;
services.xserver = {
desktopManager.gnome.enable = true;
displayManager.gdm.enable = true;
#displayManager.autoLogin = {
# enable = true;
# user = mainUser;
#};
};
programs.dconf.enable = true;
home-manager.users.${mainUser}.dconf = {
enable = true;
settings = {
"org/gnome/terminal/legacy" = {
mnemonics-enabled = false;
theme-variant = "dark";
};
"org/gnome/desktop/interface" = {
enable-animations = false;
enable-hot-corners = false;
show-battery-percentage = true;
};
"org/gnome/desktop/peripherals/touchpad" = {
edge-scrolling-enabled = false;
natural-scroll = false;
send-events = "enabled";
tap-to-click = true;
two-finger-scrolling-enabled = true;
};
"org/gnome/desktop/session".idle-delay = 900;
"org/gnome/desktop/wm/keybindings" = {
close=["<Shift><Super>c"];
minimize=["<Super>n"];
move-to-workspace-1=["<Shift><Super>1"];
move-to-workspace-2=["<Shift><Super>2"];
move-to-workspace-3=["<Shift><Super>3"];
move-to-workspace-4=["<Shift><Super>4"];
panel-run-dialog=["<Super>r"];
switch-to-workspace-1=["<Super>1"];
switch-to-workspace-2=["<Super>2"];
switch-to-workspace-3=["<Super>3"];
switch-to-workspace-4=["<Super>4"];
toggle-fullscreen=["<Super>f"];
};
"org/gnome/desktop/wm/preferences".num-workspaces = 4;
"org/gnome/settings-daemon/plugins/color".night-light-enabled = true;
"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [ "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"];
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = {
binding = "<Super>Return";
command = "gnome-terminal";
name = "terminal";
};
};
};
}

View File

@ -12,10 +12,9 @@
services.pipewire = {
enable = true;
systemWide = true;
# systemWide = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
jack.enable = true;
};
}

View File

@ -0,0 +1,44 @@
{ pkgs, lib, ... }:
{
imports = [
./base.nix
];
users.users.kiosk = {
# packages = [ pkgs.chromium pkgs.vscode ];
group = "kiosk";
isNormalUser = true;
uid = 1003;
extraGroups = [ "wheel" "audio" "pulse" "pipewire" ];
};
users.groups.kiosk.gid = 989 ;
services.xserver = {
enable = true;
windowManager = lib.mkForce { awesome.enable = false; };
displayManager.gdm.enable = true;
displayManager.gdm.autoSuspend = false;
displayManager.autoLogin = {
enable = true;
user = lib.mkForce "kiosk";
};
displayManager.defaultSession = "gnome";
desktopManager.gnome.enable = true;
};
systemd.targets.sleep.enable = false;
systemd.targets.suspend.enable = false;
systemd.targets.hibernate.enable = false;
systemd.targets.hybrid-sleep.enable = false;
environment.systemPackages = [ pkgs.gnomeExtensions.appindicator ];
services.dbus.packages = with pkgs; [ gnome2.GConf gnome3.gnome-settings-daemon ];
services.pipewire.systemWide = lib.mkForce false;
services.pipewire.config.pipewire-pulse = {
"pulse.properties"."server.address" = [ "unix:native" "tcp:4713" ];
};
}

View File

@ -5,11 +5,11 @@
./base.nix
];
users.users.kiosk = {
packages = [ pkgs.chromium pkgs.vscode ];
packages = with pkgs;[ chromium vscode spotify tartube-yt-dlp ];
group = "kiosk";
isNormalUser = true;
uid = 1003;
extraGroups = [ "wheel" "audio" "pulse" ];
extraGroups = [ "wheel" "audio" "pulse" "pipewire" ];
};
users.groups.kiosk.gid = 989 ;
services.xserver = {
@ -31,7 +31,10 @@
};
environment.systemPackages = [ pkgs.gnomeExtensions.appindicator ];
environment.systemPackages = [
pkgs.gnomeExtensions.appindicator pkgs.pavucontrol pkgs.jellyfin-media-player pkgs.chromium pkgs.firefox pkgs.kodi
pkgs.pavucontrol
];
services.dbus.packages = with pkgs; [ gnome2.GConf gnome3.gnome-settings-daemon ];
systemd.services.xset-off = {
@ -45,5 +48,9 @@
Restart = "on-failure";
};
};
services.pipewire.systemWide = lib.mkForce false;
services.pipewire.config.pipewire-pulse = {
"pulse.properties"."server.address" = [ "unix:native" "tcp:4713" ];
};
}

View File

@ -61,6 +61,8 @@ direnv allow
size = 900001;
save = 900001;
ignoreDups = true;
ignoreSpace = true;
extended = true;
share = true;
};
@ -77,31 +79,32 @@ direnv allow
xo = "mimeopen";
nmap = "nmap -oN $HOME/loot/scan-`date +\%s`.nmap -oX $HOME/loot/scan-`date +%s`.xml";
};
# navi package does not come with the navi.plugin.zsh anymore so we use .src
#zplug = {
# enable = true;
# plugins = [
# { name = "denisidoro/navi" ; }
# { name = "zsh-users/zsh-autosuggestions" ; }
# ];
#};
initExtra = ''
bindkey -e
zle -N edit-command-line
# ctrl-x ctrl-e
bindkey '^xe' edit-command-line
bindkey '^x^e' edit-command-line
# shift-tab
bindkey '^[[Z' reverse-menu-complete
bindkey "\e[3~" delete-char
zstyle ':completion:*' menu select
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_FIND_NO_DUPS
compdef _pass brain
zstyle ':completion::complete:brain::' prefix "$HOME/brain"
compdef _pass secrets
zstyle ':completion::complete:secrets::' prefix "$HOME/.secrets-pass/"
# navi
. ${pkgs.navi.src}/shell/navi.plugin.zsh
# ctrl-x ctrl-e
autoload -U compinit && compinit
autoload -U edit-command-line
zle -N edit-command-line
bindkey '^xe' edit-command-line
bindkey '^x^e' edit-command-line
'';
};
};

View File

@ -1,8 +1,12 @@
{ pkgs, ... }:
let
#dev = "/dev/web_cam";
dev = "/dev/video0";
in
{
services.mjpg-streamer = {
enable = true;
inputPlugin = "input_uvc.so -d /dev/web_cam -r 1280x960";
inputPlugin = "input_uvc.so -d ${dev} -r 1280x960";
};
users.users.octoprint.extraGroups = [ "video" ];
# allow octoprint to access /dev/vchiq

View File

@ -1,10 +1,12 @@
let
inherit (import ../lib) btn_cycle_light;
schlafzimmer_komode = "light.schlafzimmer_komode_osram";
schlafzimmer_button = "sensor.schlafzimmer_btn2_click";
in {
services.home-assistant.config.automation = [
# (btn_cycle_light "light.arbeitszimmerbeleuchtung" "arbeitszimmer_btn1")
(btn_cycle_light "light.schlafzimmer_komode_osram" "schlafzimmer_btn2" 128)
{
alias = "toggle keller";
trigger = {
@ -32,21 +34,35 @@ in {
service = "light.toggle";
data = {
entity_id = "light.keller_osram";
brightness = 50;
brightness = 25;
};
};
}
# (btn_cycle_light "light.wohnzimmerbeleuchtung" "wohnzimmer_btn3")
{
alias = "Turn of all lights via schlafzimmer_btn2 double click";
alias = "Dim Toggle schlafzimmer komode";
trigger = {
platform = "state";
entity_id = "sensor.schlafzimmer_btn2_click";
entity_id = schlafzimmer_button;
to = "single";
};
action = {
service = "light.toggle";
entity_id = schlafzimmer_komode;
brightness = 1;
};
}
{
alias = "Bright Toggle schlafzimmer komode";
trigger = {
platform = "state";
entity_id = schlafzimmer_button;
to = "double";
};
action = {
service = "light.turn_off";
entity_id = "all";
service = "light.toggle";
entity_id = schlafzimmer_komode;
brightness = 255;
};
}
];

View File

@ -6,7 +6,7 @@
let
schranklicht = [
"light.wohnzimmer_schrank_osram"
"light.wohnzimmer_komode_osram"
# "light.wohnzimmer_komode_osram"
];
weihnachtslicht = "light.wohnzimmer_fenster_lichterkette_licht";
fernsehlicht = "light.wled";
@ -31,8 +31,8 @@ in
automation =
[
(turn_on schranklicht "-00:30:00")
#(turn_on weihnachtslicht "-00:30:00")
(turn_on fernsehlicht "-00:00:00")
(turn_on weihnachtslicht "-00:00:00")
#(turn_on fernsehlicht "-00:00:00")
{ alias = "Always turn off the urlaub lights at ${final_off}";
trigger = [

View File

@ -7,7 +7,7 @@ Heute ist {{ weekday }}, du solltest gar nicht arbeiten!
{% else %}
Willkommen auf Arbeit Felix.
{% endif -%}
Das aktuell gewählte Projekt ist {{ states("sensor.felix_project") }}.
Dein Projekt ist {{ states("sensor.felix_project") }}.
{% set inside = states("sensor.wohnzimmer_temp_temperature") | float | round(2) -%}
{% set outside = states("sensor.dark_sky_temperature") | float | round(2) -%}

View File

@ -17,6 +17,7 @@ in {
./zigbee2mqtt.nix
# ./multi/flurlicht.nix
./multi/kurzzeitwecker.nix
./intents
./multi/the_playlist.nix
./multi/heizung.nix
# ./multi/fliegen-couter.nix
@ -92,6 +93,7 @@ in {
{ type = "homeassistant"; }
];
};
tasmota = {};
binary_sensor = [
{ platform = "workday";
name = "Arbeitstag";

View File

@ -0,0 +1,30 @@
{ config, pkgs, lib, ... }:
let
confdir = "/var/lib/homeassistant-docker";
in {
imports = [
./nginx.nix
./mqtt.nix
./signal-rest
./signal-rest/service.nix
];
networking.firewall.allowedTCPPorts = [ 8123 ];
state = [ "/var/lib/hass/known_devices.yaml" ];
virtualisation.oci-containers.containers.hass = {
image = "homeassistant/home-assistant:latest";
environment = {
TZ = "Europe/Berlin";
UMASK = "007";
};
extraOptions = ["--net=host" ];
volumes = [
"${confdir}:/config"
#"/data/music:/config/media"
];
};
systemd.tmpfiles.rules = [
#"f ${confdir}/docker-run 0770 kiosk kiosk - -"
"d ${confdir} 0770 kiosk kiosk - -"
];
}

View File

@ -0,0 +1,35 @@
{
services.home-assistant.config = {
intent_script = {
GetTime.speech.text = ''
Es ist {{ now().hour }} Uhr {{ now().minute }}
'';
GutenMorgen.speech.text = ''
Einen wunderschönen Guten Morgen wünsche ich dir
'';
WieGehtEsDir.speech.text = ''
Mir geht es sehr gut, und dir?
'';
Statusreport.speech.text = builtins.readFile ./statusbericht.txt.j2;
StartMusic = {
speech.text = "Spiele {{ music }} musik";
action_async = [
{
service = "media_player.play_media";
data_template = {
entity_id = "media_player.{{ _intent.siteId }}";
media_content_id = builtins.readFile ./music_chooser.txt.j2;
media_content_type = "music";
};
}
];
};
GetWeather = {
#speech.text = ''
# {{ states('sensor.openweathermap_weather') }} bei {{ states('sensor.openweathermap_temperature') }} Grad
#'';
speech.text = "{{ states('sensor.swr_prognose') }}";
};
};
};
}

View File

@ -0,0 +1,13 @@
{% if music == "lounge" -%}
https://cast1.asurahosting.com/proxy/julien/stream.mp3
{% elif music == "lassulus" -%}
http://radio.lassul.us:8000/radio.mp3
{% elif music == "groove" -%}
http://ice2.somafm.com/groovesalad-128.mp3
{% elif music == "swr3" -%}
https://liveradio.swr.de/sw282p3/swr3/play.mp3
{% elif music == "swr1" -%}
https://liveradio.swr.de/sw282p3/swr1bw/play.mp3
{% elif music == "radio" -%}
https://liveradio.swr.de/sw282p3/swr1bw/play.mp3
{% endif %}

View File

@ -0,0 +1,37 @@
{% set arbeit_heute = is_state("binary_sensor.arbeitstag","on") -%}
{% set weekday = ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][now().weekday()] -%}
{% set is_friday = now().weekday() == 4 %}
Dies ist deine Persönliche Zusammenfassung
{% set inside = states("sensor.wohnzimmer_temp_temperature") | float | round(2) -%}
{% set outside = states("sensor.dark_sky_temperature") | float | round(2) -%}
{% set arbeit_morgen = is_state("binary_sensor.arbeitstag_morgen","on") -%}
Die Wetteraussichten: {{ states("sensor.dark_sky_hourly_summary") | replace(".","")}} bei {{ states("sensor.dark_sky_temperature") }} Grad mit {{ states("sensor.dark_sky_humidity") | round(0) }}% Luftfeuchtigkeit.
{% if states("calendar.abfall_papiermuell") == "on" %}
Heute ist Papiermuell, bring noch schnell dein Papier raus
{% endif %}
{% if states("calendar.abfall_restmuell") == "on" %}
Ausserdem ist heute Restmuell.
{% endif -%}
{% if ( outside < inside ) and ( outside > 18 ) %}
Draussen ist es gerade {{ ((inside - outside) | round(1) )}} gerade kühler
{% endif -%}
{% set current_count = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_count") %}
{% for i in range(current_count) %}
{% set idx = i + 1 %}
{% set headline = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_" ~ idx ~ "_headline") %}
{% set description = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_" ~ idx ~ "_description") %}
{% set level = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_" ~ idx ~ "_level") %}
{% set time_start = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_" ~ idx ~ "_start") %}
{% set time_end = state_attr("sensor.dwd_weather_warnings_current_warning_level", "warning_" ~ idx ~ "_end") %}
Wetterwarnung {{idx}}: {{ headline }} Stufe {{level}} von {{ time_start.strftime("%H:%M") ~ " bis " ~ time_end.strftime("%H:%M") }} Uhr
{{ description }}
{% endfor %}
{% if is_friday %}
Endlich ist Freitag!
{% endif -%}

View File

@ -27,12 +27,11 @@ in
#}
{ delay.seconds = 1; }
{ delay = ''
{% set duration = state_attr("${entity}","media_duration") %}
{% set seconds = duration % 60 %}
{% set duration = state_attr("${entity}","media_duration") or 0 %}
{% set seconds = (duration % 60 ) %}
{% set minutes = (duration / 60)|int % 60 %}
{% set hours = (duration / 3600)|int %}
{{ "%02i:%02i:%02i"|format(hours, minutes, seconds)}}
'';
}
{

View File

@ -6,10 +6,30 @@ let
wohnzimmer_deko = [
"light.wohnzimmer_fernseher_led_strip" # led um fernseher
"light.wohnzimmer_lichterkette_led_strip" # led um fernsehwand
"light.kinderzimmer_lichterkette_licht" # led um fenster
"light.wohnzimmer_fenster_lichterkette_licht" # led um fenster
];
in {
imports = [ ./tint_wohnzimmer.nix ];
services.home-assistant.config.scene = [
{ name = "Wohnzimmer Abendlicht";
id = "living_room_evening";
entities = {
"light.wohnzimmer_komode_osram_light" = {
state = "on";
brightness = 128;
};
"light.wohnzimmer_schrank_osram_light" = {
state = "on";
brightness = 128;
};
"light.wohnzimmer_fenster_lichterkette_licht" = "on";
"light.wohnzimmer_fernseher_led_strip" = {
state = "on";
};
};
}
];
services.home-assistant.config.wled = {};
services.home-assistant.config.light = [
{
@ -22,6 +42,11 @@ in {
name = "Wohnzimmer Deko";
entities = wohnzimmer_deko;
}
{
platform = "group";
name = "living_room_lights";
entities = wohnzimmerbeleuchtung ++ wohnzimmer_deko;
}
];
}

View File

@ -3,11 +3,11 @@ let
in {
services.home-assistant.config = {
notify = [
{
platform = "nfandroidtv";
name = "FireTV Wohnzimmer Notification";
host = firetv_stick;
}
#{
#platform = "nfandroidtv";
#name = "FireTV Wohnzimmer Notification";
#host = firetv_stick;
#}
];
media_player = [
#{
@ -16,12 +16,12 @@ in {
# host = firetv_stick;
#}
# Configuration needs to be done by hand via web interface "integration"
{ platform = "androidtv";
name = "FireTV Stick Android";
device_class = "firetv";
host = firetv_stick;
port = 5555;
}
#{ platform = "androidtv";
# name = "FireTV Stick Android";
# device_class = "firetv";
# host = firetv_stick;
# port = 5555;
#}
];
};
}

View File

@ -5,7 +5,7 @@
services.mosquitto = {
enable = true;
persistence = false;
settings.max_keepalive = 60;
settings.max_keepalive = 1060;
listeners = [
{
port = 1883;

View File

@ -9,128 +9,80 @@
let
button = "sensor.zigbee_btn2_click";
notify = "notify.signal_home";
# für {{ _intent.siteId }} - name of the rhasspy instance: arbeitszimmer
in
{
services.home-assistant.config = {
timer.kurzzeitwecker =
{
name = "Zigbee Kurzzeitwecker";
duration = 300;
automation = [];
timer.kurzzeitwecker = {
name = "Wecker Wohnung";
};
script.add_5_minutes_to_kurzzeitwecker =
{
alias = "Add 5 minutes to kurzzeitwecker";
sequence = [
{ service = "timer.pause";
entity_id = "timer.kurzzeitwecker";
}
{ service = "timer.start";
data_template = {
entity_id = "timer.kurzzeitwecker";
duration = ''
{% set r = state_attr('timer.kurzzeitwecker', 'remaining') ~ '-0000' %}
{% set t = strptime(r, '%H:%M:%S.%f%z') %}
{{ (as_timestamp(t) + 300) | timestamp_custom('%H:%M:%S', false) }}
'';
};
}
];
timer.wecker_arbeitszimmer = {
name = "Wecker Arbeitszimmer";
};
automation =
[
{
alias = "Start Timer 5min";
trigger = {
platform = "state";
entity_id = button;
to = "single";
};
condition =
{ condition = "state";
entity_id = "timer.kurzzeitwecker";
state = "idle";
};
timer.wecker_wohnzimmer = {
name = "Wecker Wohnzimmer";
};
intent = {};
intent_script = {
TimerjobStart = {
speech.text = ''
{% set h = hours|default('0')|string %}
{% set m = minutes|default('0')|string %}
{% if h == "0" %}
Wecker gestellt {{ m }} Minuten
{% elif m == "0" %}
Wecker gestellt {{ h }} Stunden
{% else %}
Wecker gestellt {{ h }} Stunden und {{ m }} Minuten
{% endif %}
'';
action = [
{
service = "timer.start";
data.entity_id = "timer.kurzzeitwecker";
data.duration = ''
{% set h = hours|default("0")|int %}
{% set m = minutes|default("0")|int %}
{{ "%02d" | format(h) }}:{{ "%02d" | format(m) }}:00
'';
}
];
};
TimerjobRemaining = {
speech.text = ''
{% set timer = states('timer.kurzzeitwecker') %}
{% if timer == 'idle' %}
Wecker läuft nicht
{% elif timer == 'active' %}
{% set remaining = as_timestamp( state_attr('timer.kurzzeitwecker','finishes_at') )-( as_timestamp(now())) %}
{% set s = ((remaining % 60)) | int %}
{% set m = ((remaining % 3600) / 60) | int %}
{% set h = ((remaining % 86400) / 3600) | int %}
{% if h == 0 %}
Es verbleiben {{ m }} Minuten und {{ s }} Sekunden
{% elif m == 0 %}
Es verbleiben {{ h }} Stunden
{% elif m == 0 and h == 0 %}
Es verbleiben {{ s }} Sekunden
{% else %}
Es verbleiben {{ h }} Stunden {{ m }} Minuten
{% endif %}
{% endif %}
'';
};
TimerjobStop = {
speech.text = ''
Wecker gestoppt
'';
action = [
{ service = "timer.start";
entity_id = "timer.kurzzeitwecker";
data.duration = "00:05:00";
}
{
service = notify;
data.message = "Timer gestartet {{state_attr('timer.kurzzeitwecker', 'remaining') }}, verbleibend ";
{ service = "timer.cancel";
data.entity_id = "timer.kurzzeitwecker";
}
];
}
{
alias = "Add Timer 5min";
trigger = {
platform = "state";
entity_id = button;
to = "single";
};
condition =
{ condition = "state";
entity_id = "timer.kurzzeitwecker";
state = "active";
};
action = [
{ service = "homeassistant.turn_on";
entity_id = "script.add_5_minutes_to_kurzzeitwecker";
}
{
service = notify;
data.message = ''Timer um 5 minuten verlängert, {{ state_attr('timer.kurzzeitwecker', 'remaining') | truncate(9,True," ") }} verbleibend '';
}
];
}
{
alias = "Stop timer on double click";
trigger = [
{
platform = "state";
entity_id = button;
to = "double";
}
{
platform = "state";
entity_id = button;
to = "triple";
}
];
condition =
{
condition = "state";
entity_id = "timer.kurzzeitwecker";
state = "active";
};
action = [
{
service = "timer.cancel";
entity_id = "timer.kurzzeitwecker";
}
{
service = notify;
data.message = "Timer gestoppt, abgebrochen";
}
];
}
{
alias = "Timer Finished";
trigger = {
platform = "event";
event_type = "timer.finished";
event_data.entity_id = "timer.kurzzeitwecker";
};
action = [
{
service = notify;
data.message = "Timer beendet";
}
];
}
];
};
};
};
}

View File

@ -40,5 +40,16 @@
{ platform = "accuweather";
api_key = "!secret accuweather";
}
{ platform = "scrape";
resource = "https://www.swr.de/wetter/wetter-liste-swr-100.html";
name = "SWR Prognose";
select = "p[data-refresh=\"weather-headline\"]";
}
{ platform = "scrape";
resource = "https://www.swr.de/wetter/wetter-liste-swr-100.html";
name = "SWR Prognose Langtext";
select = "p[data-refresh=\"weather-text\"]";
}
];
}

View File

@ -1,66 +1,34 @@
{ lib, config, ... }:
let
port = 8096;
in
{
services.jellyfin.enable = true;
services.jellyfin.openFirewall = true;
# services.jellyfin.openFirewall = true;
networking.firewall.interfaces.wiregrill = {
allowedTCPPorts = [ 80 port 8920 ];
allowedUDPPorts = [ 1900 7359 ];
};
state = [ "/var/lib/jellyfin" ];
users.users.${config.services.jellyfin.user}.extraGroups = [ "download" "video" "render" ];
systemd.services.jellyfin = {
after = [ "media-cloud.mount" ];
serviceConfig = rec {
RequiresMountFor = [ "/media/cloud" ];
SupplementaryGroups = lib.mkForce [ "video" "render" "download" ];
UMask = lib.mkForce "0077";
Type = lib.mkForce "simple";
StateDirectory = lib.mkForce "jellyfin";
StateDirectoryMode = lib.mkForce "0700";
CacheDirectory = lib.mkForce "jellyfin";
CacheDirectoryMode = lib.mkForce "0700";
WorkingDirectory = lib.mkForce "/var/lib/jellyfin";
Restart = lib.mkForce "on-failure";
TimeoutSec = lib.mkForce 15;
SuccessExitStatus = lib.mkForce ["0" "143"];
# Security options:
NoNewPrivileges = lib.mkForce true;
SystemCallArchitectures = lib.mkForce "native";
# AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = lib.mkForce [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
RestrictNamespaces = lib.mkForce false;
RestrictRealtime = lib.mkForce true;
RestrictSUIDSGID = lib.mkForce true;
ProtectControlGroups = lib.mkForce false;
ProtectHostname = lib.mkForce true;
ProtectKernelLogs = lib.mkForce false;
ProtectKernelModules = lib.mkForce false;
ProtectKernelTunables = lib.mkForce false;
LockPersonality = lib.mkForce true;
PrivateTmp = lib.mkForce false;
# needed for hardware accelaration
PrivateDevices = lib.mkForce false;
PrivateUsers = lib.mkForce true;
RemoveIPC = lib.mkForce true;
SystemCallFilter = lib.mkForce [
"~@clock"
"~@aio"
"~@chown"
"~@cpu-emulation"
"~@debug"
"~@keyring"
"~@memlock"
"~@module"
"~@mount"
"~@obsolete"
"~@privileged"
"~@raw-io"
"~@reboot"
"~@setuid"
"~@swap"
];
SystemCallErrorNumber = lib.mkForce "EPERM";
};
};
services.nginx.virtualHosts."jelly" = {
serverAliases = [
"jelly.lan" "movies.lan"
"jelly.makefu.w" "makefu.omo.w"
];
locations."/" = {
proxyPass = "http://localhost:${toString port}";
proxyWebsockets = true;
};
};
}

View File

@ -9,8 +9,7 @@ in
MusicFolder = "/media/cryptX/music/kinder";
Address = "0.0.0.0";
};
systemd.services.navidrome.after = [ "media-cryptX.mount" "cryptsetup.target"
"local-fs.target" "remote-fs.target" ];
systemd.services.navidrome.serviceConfig.RequiresMountFor = [ "/media/cryptX" ];
state = [ "/var/lib/navidrome" ];
# networking.firewall.allowedTCPPorts = [ 4040 ];

View File

@ -70,15 +70,18 @@ in
PHOTOPRISM_HTTP_PORT = port; # Built-in Web server port
PHOTOPRISM_HTTP_COMPRESSION = "gzip"; # Improves transfer speed and bandwidth utilization (none or gzip)
PHOTOPRISM_DEBUG = "false"; # Run in debug mode (shows additional log messages)
PHOTOPRISM_PUBLIC = "true"; # No authentication required (disables password protection)
# PHOTOPRISM_PUBLIC = "true"; # No authentication required (disables password protection)
PHOTOPRISM_READONLY = "false"; # Don't modify originals directory (reduced functionality)
PHOTOPRISM_EXPERIMENTAL = "true"; # Enables experimental features
PHOTOPRISM_DISABLE_WEBDAV = "false"; # Disables built-in WebDAV server
# PHOTOPRISM_DISABLE_WEBDAV = "false"; # Disables built-in WebDAV server
PHOTOPRISM_DISABLE_SETTINGS = "false"; # Disables Settings in Web UI
PHOTOPRISM_DISABLE_TENSORFLOW = "false"; # Disables using TensorFlow for image classification
PHOTOPRISM_DARKTABLE_PRESETS = "false"; # Enables Darktable presets and disables concurrent RAW conversion
PHOTOPRISM_DETECT_NSFW = "false"; # Flag photos as private that MAY be offensive (requires TensorFlow)
PHOTOPRISM_UPLOAD_NSFW = "true"; # Allow uploads that MAY be offensive
PHOTOPRISM_AUTH_MODE = "password";
PHOTOPRISM_ADMIN_USER = "admin";
PHOTOPRISM_ADMIN_PASSWORD = "admin";
#PHOTOPRISM_DATABASE_DRIVER = "postgres";
#PHOTOPRISM_DATABASE_SERVER = "postgres-prism:5432";

View File

@ -0,0 +1,40 @@
{ lib,config, ... }:
# uses alsa instead of pulseaduio server
let
profiles = "/var/lib/rhasspy";
in
{
systemd.services.docker-rhasspy.after = [ "network-online.target" ];
virtualisation.oci-containers.containers.rhasspy = {
image = "rhasspy/rhasspy:latest";
environment = {
TZ = "Europe/Berlin";
PULSE_SERVER = "tcp:${ config.krebs.build.host.name }:4713";
};
ports = [
"12101:12101"
];
volumes = [
"/etc/localtime:/etc/localtime:ro"
"${profiles}:/profiles"
];
cmd = [ "--user-profiles" "/profiles" "--profile" "de" ];
extraOptions = [
"--device=/dev/snd:/dev/snd"
"--group-add=audio"
];
};
systemd.tmpfiles.rules = [
"d ${profiles} 0770 root root - -"
];
# required to allow rhasspy to connect to pulse server
# hardware.pulseaudio.enable = lib.mkForce false;
networking.firewall.allowedTCPPorts = [ 4713 ];
}

View File

@ -0,0 +1,23 @@
{ pkgs, ... }:
let
cfg = pkgs.writeText "hcl-config.json" (builtins.toJSON {
engine = "rhasspy";
pathToConfig = "/var/lib/rhasspy/de/profile.json";
hardware = "respeaker4MicArray";
pattern = "fake-name";
enableDoA = false;
});
in {
systemd.services.HermesLedControl = {
description = "Led Server for ReSpeaker 4-array";
after = [ "network-online.target" "docker-rhasspy.service" ] ;
wantedBy = [ "multi-user.target" ];
serviceConfig = {
# User = "nobody"; # need a user with permissions to run nix-shell
ExecStart = "${pkgs.HermesLedControl}/bin/HermesLedControl --hermesLedControlConfig=${toString cfg}";
Restart = "always";
RestartSec = 10;
PrivateTmp = true;
};
};
}

View File

@ -32,6 +32,10 @@ in
include_device_information = true;
client_id = "zigbee2mqtt";
};
availability = {
active.timeout = 10;
passive.timeout = 1500;
};
frontend = {
port = webport;
};

View File

@ -0,0 +1,7 @@
{ pkgs, ... }:
{
users.users.makefu = {
extraGroups = [ "cdrom" ];
packages = [ pkgs.glyr pkgs.abcde ];
};
}

View File

@ -0,0 +1,6 @@
# https://bugzilla.kernel.org/show_bug.cgi?id=198129
{
boot.extraModprobeConfig = ''
options snd_usb_audio ignore_ctl_error=1
'';
}

View File

@ -37,7 +37,7 @@
emulateWheel = true;
};
services.tlp.enable = true;
services.tlp.enable = ! config.services.power-profiles-daemon.enable;
services.tlp.settings = {
# BUG: http://linrunner.de/en/tlp/docs/tlp-faq.html#erratic-battery
START_CHARGE_THRESH_BAT0 = 95;

Some files were not shown because too many files have changed in this diff Show More