Merge branch 'feature/flakes'

feature/hass
Ingolf Wagner 2021-11-01 09:24:45 +01:00
commit 6dc18d06e1
Signed by: palo
GPG Key ID: 76BF5F1928B9618B
298 changed files with 5865 additions and 4682 deletions

View File

@ -1,10 +0,0 @@
{
"url": "https://github.com/NixOS/nixpkgs.git",
"rev": "6bfe71f2a4e2e425dee26b25d2309f341ff1600d",
"date": "2021-09-02T17:55:10+02:00",
"path": "/nix/store/wmg7a97b7ql8kj413wkvh2pmvl5m4nkd-nixpkgs",
"sha256": "1mpf700fqlzyj6vsy2c329zlgbk9g6giwiyb2g0yhc0a78h72g8l",
"fetchSubmodules": false,
"deepClone": false,
"leaveDotGit": false
}

View File

@ -1,10 +0,0 @@
{
"url": "https://github.com/NixOS/nixpkgs.git",
"rev": "a51aa6523bd8ee985bc70987909eff235900197a",
"date": "2021-09-04T10:19:48-03:00",
"path": "/nix/store/qylkdn96ah6r3mhh5m0p3yv236nxdbsa-nixpkgs",
"sha256": "1rpikl60v179gsshqfrr4xwz42db5g87scm2v2hk3v3jys9dqrgc",
"fetchSubmodules": false,
"deepClone": false,
"leaveDotGit": false
}

View File

@ -1,7 +0,0 @@
{
"url": "https://git.ingolf-wagner.de/krebs/krops.git",
"rev": "2e93a93ac264a480b427acc2684993476732539d",
"date": "2018-09-19T19:57:26+02:00",
"sha256": "1s6b2cs60xa270ynhr32qj1rcy3prvf9pidap0qbbvgg008iafxk",
"fetchSubmodules": false
}

View File

@ -1,10 +0,0 @@
{
"url": "https://cgit.krebsco.de/nix-writers/",
"rev": "fc8a3802a0777a5f43a9a2fe0f5848ecaeb555a1",
"date": "2018-10-27T14:45:48+02:00",
"path": "/nix/store/81f2li00frwb29kzk7wjw7b87l65s8bg-nix-writers",
"sha256": "1iy207rcbz9nv9bf64025ypy38x8mwzl6snbmbrq347h6vvs0ksc",
"fetchSubmodules": false,
"deepClone": false,
"leaveDotGit": false
}

View File

@ -1,10 +0,0 @@
{
"url": "https://github.com/nix-community/nixos-generators.git",
"rev": "ef1e4480cf8af45cfdeac597b2f1b1af33923e93",
"date": "2019-01-18T10:41:01+01:00",
"path": "/nix/store/61wmz4fvlyxnxhw9smkl897rhavj06ry-nixos-generators-ef1e448",
"sha256": "0ymzp4pmpkjjjg5h8d45gv8avy4wh1dj0v238i2cz3jp3j489ik9",
"fetchSubmodules": false,
"deepClone": false,
"leaveDotGit": false
}

View File

@ -1,228 +0,0 @@
{ pkgs, lib, config, ... }: {
services.mqtt.light-control.enable = true;
services.mqtt.light-control.loglevel = "debug";
services.mqtt.light-control.config = {
credentials = {
host = "tcp://localhost:1883";
user = "homeassistant";
password = "hallo";
};
scenes = [
{
name = "up-dark";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "half";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
}
{
name = "down";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "up-bright";
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "outside";
room_tracking_enabled = false;
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "night";
room_tracking_enabled = false;
brightness = 25;
ignored_sensors =
[ "zigbee2mqtt/motion_sensor_7" "zigbee2mqtt/door_sensor_4" ];
}
];
sensors = let
door = { topic, room }: {
topic = topic;
key = "contact";
room = room;
invert_state = true;
delay = 90;
};
motion = { topic, room }: {
topic = topic;
key = "occupancy";
room = room;
delay = 60;
};
in [
(motion {
topic = "zigbee2mqtt/motion_sensor_1";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_2";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_6";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_8";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_7";
room = "sleeping_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_5";
room = "kitchen";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_4";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_1";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_5";
room = "sleeping_room";
})
(door {
# house door
topic = "zigbee2mqtt/door_sensor_4";
room = "floor";
})
];
switches = let
sonoff = { id, rooms, delay ? 0 }: {
topic = "stat/${id}/RESULT";
key = "POWER";
rooms = rooms;
delay = delay;
command = {
command = "{{state}}";
init_command = "(null)";
topic = "cmnd/${id}/POWER";
on = "ON";
off = "OFF";
};
};
light = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
command = ''{"state":"{{state}}","brightness":{{brightness}}}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
led = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
# Configure it once to the color you like
# {"state":"{{state}}","brightness":{{brightness}},"color":{"hex":"#FFFFFF},"color_temp":255","transition":0}
command = ''
{"state":"{{state}}","brightness":{{brightness}},"transition":0}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
in [
(light {
topic = "zigbee2mqtt/light_2";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_4";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_5";
rooms = [ "storage_room" ];
})
(light {
topic = "zigbee2mqtt/light_7";
rooms = [ "sleeping_room" ];
})
(led {
topic = "zigbee2mqtt/led_1";
rooms = [ "office_room" ];
})
(led {
topic = "zigbee2mqtt/led_2";
rooms = [ "kitchen" ];
})
#(sonoff {
# id = "PAL01";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL03";
# rooms = [ "living_room" ];
#})
#(sonoff {
# id = "PAL04";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL06";
# rooms = [ "kitchen" ];
#})
## monitor and speakers
#(sonoff {
# id = "PAL07";
# rooms = [ "bed_room" ];
# delay = 180;
#})
#(sonoff {
# id = "PAL08";
# rooms = [ "bed_room" ];
# delay = 180;
#})
];
};
}

View File

@ -1,173 +0,0 @@
{ lib, config, pkgs, ... }:
let
folderPath = config.services.home-assistant.configDir;
# find symbols with
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=<keywords>&apikey=<api_key>
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
#
# example:
# --------
# stocks = [
# {
# symbol = "GOOGL";
# name = "google";
# friendly_name = "Google";
# currency = "$";
# # I own 50 and bought at a price of 1000
# own = {
# pieces = 50;
# price = 1000;
# };
# }
# ];
stocks = import <secrets/home-assistant/stocks>;
filePath = name: "${folderPath}/stock_${name}.json";
cleanup_list = list: lib.filter (entry: entry != { }) (lib.flatten list);
in {
services.homeAssistantConfig = {
sensor = cleanup_list (map ({ name, currency, own ? { }, ... }: [
{
platform = "file";
name = "stock_${name}";
file_path = filePath name;
value_template = "{{ value_json.price}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change";
file_path = filePath name;
value_template = "{{ value_json.change}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change_percent";
file_path = filePath name;
value_template = "{{ value_json.change_percent}} %";
}
(lib.optionalAttrs (own != { }) {
platform = "file";
name = "stock_${name}_profit";
file_path = filePath name;
value_template = ''
{{ "{:,.2f}".format( value_json.price * ${toString own.pieces} - ${
toString (own.pieces * own.price)
} ) }} ${currency}'';
})
]) stocks);
homeassistant = {
whitelist_external_dirs = [ folderPath ];
customize = builtins.listToAttrs (cleanup_list (map
({ name, own ? { }, ... }: [
{
name = "sensor.stock_${name}";
value = {
icon = "mdi:cash-usd-outline";
friendly_name = "Price";
};
}
{
name = "sensor.stock_${name}_change";
value = {
icon = "mdi:radar";
friendly_name = "Difference";
};
}
{
name = "sensor.stock_${name}_change_percent";
value = {
icon = "mdi:radar";
friendly_name = "Percent";
};
}
(lib.optionalAttrs (own != { }) {
name = "sensor.stock_${name}_profit";
value = {
icon = "mdi:radar";
friendly_name = "Profit";
};
})
]) stocks));
};
group = (builtins.listToAttrs (map
({ name, friendly_name, own ? { }, ... }: {
name = "stock_${name}";
value = {
name = "${friendly_name} Aktie";
entities = [
"sensor.stock_${name}"
"sensor.stock_${name}_change"
"sensor.stock_${name}_change_percent"
] ++ (lib.optional (own != { }) "sensor.stock_${name}_profit");
};
}) stocks));
};
systemd.services = let
pullService = { name, symbol, currency, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
before = [ "home-assistant.service" ];
wantedBy = [ "home-assistant.service" ];
serviceConfig = {
User = "hass";
Type = "oneshot";
};
description = "pull stock_${name} for homeassistant";
script = ''
SYMBOL="${symbol}"
CURRENCY="${currency}"
APIKEY=${
lib.fileContents <secrets/home-assistant/alphavantage/apikey>
}
${pkgs.curl}/bin/curl --location --silent \
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
| ${pkgs.jq}/bin/jq --compact-output \
'.["Global Quote"] |
{
price: .["05. price"] | tonumber,
currency: "'$CURRENCY'",
change_percent: .["10. change percent"] | .[0:-1] | tonumber,
change: .["09. change"] | tonumber,
last_date: .["07. latest trading day"],
}' \
>> ${filePath name}
# old and stupid
#${pkgs.curl}/bin/curl --location --silent \
#"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=$SYMBOL&interval=5min&apikey=$APIKEY" \
#| ${pkgs.jq}/bin/jq --compact-output \
# '.["Time Series (5min)"] | to_entries | [ .[]
# | { date : .key , value : .value["4. close"], currency: "'$CURRENCY'" } ]
# | sort_by(.date) | reverse | .[0]' \
'';
};
};
in builtins.listToAttrs (map pullService stocks);
systemd.timers = let
pullTimer = { name, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = "true";
};
};
};
in builtins.listToAttrs (map pullTimer stocks);
}

View File

@ -1,226 +0,0 @@
{ pkgs, lib, ... }:
let
# we create 3 input_boolean which get toggled by the 3 types of buttons pressed.
# input_boolean.single_${name} : single click
# input_boolean.double_${name} : double click
# input_boolean.hold_${name} : hold
# if you override these input (via states) you have to create the input yourself
# https://www.zigbee2mqtt.io/devices/WXKG12LM.html
allDevices = {
"button_a1" = {
id = "0x00158d0002b04f65";
#groups = [ "living_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a2" = {
id = "0x00158d0002b04f09";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a3" = {
id = "0x00158d0002b00e04";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define input_boolean
# --------------------
# which get toggled by the buttons
input_boolean = let stripEmpty = lib.filter (a: a != { });
in builtins.listToAttrs (stripEmpty (lib.flatten (lib.mapAttrsToList (name:
{ states ? { }, ... }: [
(lib.optionalAttrs (!lib.hasAttr "single" states) {
name = "single_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "double" states) {
name = "double_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "hold" states) {
name = "hold_${name}";
value = { icon = "mdi:toggle-switch"; };
})
]) allDevices)));
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
platform = "mqtt";
name = name;
icon = "mdi:toggle-switch";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.click }}";
}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
binary_sensor = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "motion";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = true;
payload_off = false;
value_template = "{{ value_json.occupancy }}";
}) allDevices;
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { states ? { }, ... }:
# let
# entityIds = { single ? "input_boolean.single_${name}"
# , double ? "input_boolean.double_${name}"
# , hold ? "input_boolean.hold_${name}", ... }: [
# single
# double
# hold
# ];
# in {
# entities = [ "sensor.${name}" ] ++ (entityIds states)
# ++ [ "sensor.battery_${name}" "sensor.link_${name}" ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], states ? { }, ... }:
# map (groupName: {
# "${groupName}" = if (lib.hasAttr "single" states) then
# states.single
# else
# "input_boolean.single_${name}";
# }) groups) allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
#};
# create automation
# -----------------
automation = let
# single click
toggle_single_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "single" states) then
states.single
else
"input_boolean.single_${name}";
in {
alias = "toggle single click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "single" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
# double click
toggle_double_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "double" states) then
states.double
else
"input_boolean.double_${name}";
in {
alias = "toggle double click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "double" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
# hold
toggle_hold_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "hold" states) then
states.hold
else
"input_boolean.hold_${name}";
in {
alias = "toggle hold ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "hold" == trigger.payload_json.action}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
in lib.flatten (toggle_single_button_input ++ toggle_double_button_input
++ toggle_hold_button_input);
};
}

View File

@ -1,87 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html
allDevices = {
"door_sensor_1" = { id = "0x00158d000312dc52"; };
"door_sensor_2" = { id = "0x00158d000316d5bf"; };
"door_sensor_3" = { id = "0x00158d0002f9516f"; };
"door_sensor_4" = { id = "0x00158d00031383b9"; };
"door_sensor_5" = { id = "0x00158d0003120d3e"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
binary_sensor = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "door";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = false;
payload_off = true;
value_template = "{{ value_json.contact}}";
}) allDevices;
# create groups
# -------------
group = let
# to have nice panels for every device
sensorGroups = lib.mapAttrs (name:
{ ... }: {
entities = [
"binary_sensor.${name}"
"sensor.battery_${name}"
"sensor.link_${name}"
];
}) allDevices;
# sort lights into given groups.
sortedInGroups = let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "binary_sensor.${name}"; }) groups)
allDevices));
in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in sortedInGroups // sensorGroups // {
all_sensors.entities =
lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
};
};
}

View File

@ -1,52 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/E1757.html
allDevices = {
"office_fyrtur_1" = { id = "0x680ae2fffe64fa40"; };
"office_fyrtur_2" = { id = "0x680ae2fffe91d234"; };
"bedroom_fyrtur_1" = { id = "0x680ae2fffe6e9f41"; };
"broken_fyrtur_1" = { id = "0x680ae2fffe8f6411"; };
};
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":100}'
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":15}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 0.1;
};
}) allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
};
}

View File

@ -1,80 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/SPZB0001.html
allDevices = {
"office_heater_1" = { id = "0x00158d00032f5ee4"; }; # office
"office_heater_2" = { id = "0x00158d00032f5f9f"; }; # office (kitchen)
"bedroom_heater_1" = { id = "0x00158d00032f6d1e"; }; # bed room
"storage_heater_1" = { id = "0x00158d00032f604d"; }; # abstell raum
};
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"auto","current_heating_setpoint":23}'
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"off"}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
legacy = false;
retain = false;
friendly_name = name;
transition = 1;
debounce = 0.5;
filtered_attributes = [
"battery_low"
"eurotronic_host_flags"
"eurotronic_system_mode"
#"occupied_heating_setpoint"
#"pi_heating_demand"
#"unoccupied_heating_setpoint"
];
};
}) allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
{
platform = "mqtt";
name = "temperature_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.local_temperature }}";
}
{
platform = "mqtt";
name = "pi_heating_demand_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
value_template = "{{ value_json.pi_heating_demand }}";
}
]) allDevices);
};
}

View File

@ -1,43 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/GL-C-007-1ID.html
allDevices = {
"led_1" = { id = "0x00124b001f7a5be9"; };
"led_2" = { id = "0x00124b001ee958b3"; };
};
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","brightness":255,"color":{"hex":"#00FFFF"}}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF"}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 1;
};
}) allDevices;
services.homeAssistantConfig = {
light = lib.mapAttrsToList (name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
}) allDevices;
};
}

View File

@ -1,69 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/RTCGQ01LM.html
allDevices = {
"motion_sensor_1" = { id = "0x00158d0002fbd451"; };
"motion_sensor_2" = { id = "0x00158d0002f9a6b8"; };
"motion_sensor_3" = { id = "0x00158d0002f04522"; };
"motion_sensor_4" = { id = "0x00158d0002f9a558"; };
"motion_sensor_5" = { id = "0x00158d0002f9a56f"; };
"motion_sensor_6" = { id = "0x00158d0002f9a5cb"; };
"motion_sensor_7" = { id = "0x00158d0002f9a6aa"; };
"motion_sensor_8" = { id = "0x00158d0002f04637"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, timeout ? 65, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
# should not be set below 60 seconds
occupancy_timeout = timeout;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
binary_sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [{
name = "${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.occupancy }}";
#icon = "mdi:battery-10";
payload_on = true;
payload_off = false;
device_class = "motion";
}]) allDevices);
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
};
}

View File

@ -1,107 +0,0 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/WSDCGQ11LM.html
allDevices = {
"temperature_sensor_1" = {
id = "0x00158d0002d79220";
groups = [ "living_room" ];
};
"temperature_sensor_2" = {
id = "0x00158d0002d7913d";
groups = [ "living_room" ];
};
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.temperature }}";
}
{
platform = "mqtt";
name = "humidity_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
device_class = "humidity";
value_template = "{{ value_json.humidity }}";
}
#{
# platform = "mqtt";
# name = "pressure_${name}";
# state_topic = "zigbee2mqtt/${name}";
# availability_topic = "zigbee2mqtt/bridge/state";
# unit_of_measurement = "hPa";
# device_class = "pressure";
# value_template = "{{ value_json.pressure }}";
#}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { ... }: {
# entities = [
# "sensor.${name}"
# "sensor.humidity_${name}"
# #"sensor.pressure_${name}"
# "sensor.battery_${name}"
# "sensor.link_${name}"
# ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], ... }:
# map (groupName: { "${groupName}" = "sensor.${name}"; }) groups)
# allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "sensor.${name}") allDevices;
#};
};
}

View File

@ -1,50 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
let port = 8000;
in {
# configure nginx
services.nginx = {
enable = true;
virtualHosts = {
"paste.workhorse.private" = {
locations."/" = {
proxyPass = "http://localhost:${toString port}";
extraConfig = ''
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
proxy_redirect http://localhost:${
toString port
} https://paste.workhorse.private/;
'';
};
};
};
};
krops.userKeys."bepasty" = {
user = "bepasty";
source = toString <secrets/bepasty-secret-key>;
requiredBy = [ "bepasty-server-ingolf-wagner.de-gunicorn.service" ];
};
services.bepasty = {
enable = true;
servers."ingolf-wagner.de" = {
bind = "0.0.0.0:${toString port}";
secretKeyFile = config.krops.userKeys."bepasty".target;
extraConfig = ''
PERMISSIONS = {
'${
lib.fileContents <common_secrets/bepasty/admin-password>
}': 'admin,list,create,read,delete',
}
'';
};
};
}

View File

@ -1,199 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
library = import <library> { inherit pkgs lib; };
sync-repo = library.jenkins.syncJob;
job = library.jenkins.job;
in {
environment.systemPackages = [ pkgs.cabal-install ];
services.nginx = {
enable = true;
virtualHosts = {
"jenkins.${config.networking.hostName}.private" = {
locations."/" = {
proxyPass =
"http://localhost:${toString config.services.jenkins.port}";
extraConfig = ''
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
proxy_redirect http://localhost:${
toString config.services.jenkins.port
} https://jenkins.${config.networking.hostName}.private/;
'';
};
};
};
};
krops.userKeys."accessToken" = {
user = "jenkins";
source = toString <secrets/jenkins/accessToken>;
requiredBy = [ "jenkins-job-builder.service" ];
};
services.jenkins = {
enable = true;
home = "/home/jenkins";
port = 10420;
# Plugins to Install:
# - all the plugins recommended at the setup
# - Build pipeline
# - SSH Agent
# - mattermost plugin
jobBuilder = {
enable = true;
# create an access token in the admin users panel
accessTokenFile = config.krops.userKeys."accessToken".target;
accessUser = "admin";
# https://docs.openstack.org/infra/jenkins-job-builder/definition.html#modules
nixJobs = let
# ssh username + key
gogs-id = "bc584c99-0fb7-43fb-af75-4076d64c51b2";
# ssh username + key
github-id = "bc584c99-0fb7-43fb-af75-4076d64c51b2";
# ssh username + key
sshSputnik = "d91eb57c-5bff-434c-b317-68aad46848d7";
sync-to-github = name: source: target:
sync-repo name {
url = source;
credentialsId = gogs-id;
} {
url = target;
credentialsId = github-id;
};
in [
(job "sync-retiolum" {
url = "git@github.com:krebs/retiolum.git";
credentialsId = github-id;
triggers = [{ timed = "H/30 * * * *"; }];
} [
{
"Download Files" = [
"chmod 755 hosts"
"chmod 755 -R hosts"
''
nix-shell -p curl -p gnutar -p bzip2 --run "curl https://lassul.us/retiolum-hosts.tar.bz2 | tar xvjf - || true"''
"chmod 755 -R etc.hosts"
''
nix-shell -p curl --run "curl https://lassul.us/retiolum.hosts > etc.hosts || true"''
];
}
{
"update repo" = [
''nix-shell -p git --run "git add ."''
''
nix-shell -p git --run "git -c user.name=\'Ingolf Wagner\' -c user.email=\'contact@ingolf-wagner.de\' commit -m update-`date +%Y-%m-%dT%H:%M:%S` || exit 0"''
];
}
{
Push = [{
script = ''nix-shell -p git --run "git push origin master"'';
credentialsId = github-id;
}];
}
])
(job "test-taskninja" {
url = "ssh://gogs@workhorse.private:2222/palo/taskninja.git";
credentialsId = gogs-id;
} [
{
"Create Shell" = [
''
nix-shell -p cabal2nix --run "cabal2nix --shell file://. > jenkins.nix"''
];
}
{ Update = [ ''nix-shell ./jenkins.nix --run "cabal update"'' ]; }
{
Configure = [
''nix-shell ./jenkins.nix --run "cabal configure --enable-tests"''
''
nix-shell ./jenkins.nix --run "cabal install --only-dependencies"''
];
}
{ Build = [ ''nix-shell ./jenkins.nix --run "cabal build"'' ]; }
{ Test = [ ''nix-shell ./jenkins.nix --run "cabal test"'' ]; }
])
# sync to github
# --------------
(sync-to-github "sync-radiodj"
"ssh://gogs@workhorse.private:2222/crashburn_radio/radio-dj2.git"
"git@github.com:crashburn-radio/radio-dj.git")
(sync-to-github "sync-radiodj-tracks"
"ssh://gogs@workhorse.private:2222/crashburn_radio/radio-dj-tracks.git"
"git@github.com:crashburn-radio/radio-dj-tracks.git")
(sync-to-github "sync-krops-module"
"ssh://gogs@workhorse.private:2222/nix-modules/krops.git"
"git@github.com:mrVanDalo/module.krops.git")
(sync-to-github "sync-cluster-module"
"ssh://gogs@workhorse.private:2222/nix-modules/cluster.git"
"git@github.com:mrVanDalo/module.cluster.git")
(sync-to-github "sync-backup-module"
"ssh://gogs@workhorse.private:2222/nix-modules/backup.git"
"git@github.com:mrVanDalo/module.backup.git")
(sync-to-github "sync-module-tinc"
"ssh://gogs@workhorse.private:2222/palo/nixos-tinc.git"
"git@github.com:mrVanDalo/nixos-tinc.git")
(sync-to-github "sync-memo"
"ssh://gogs@workhorse.private:2222/palo/memo.git"
"git@github.com:mrVanDalo/memo.git")
(sync-to-github "sync-diagrams-template"
"ssh://gogs@workhorse.private:2222/palo/diagrams-template.git"
"git@github.com:mrVanDalo/diagrams.git")
(sync-to-github "sync-plops"
"ssh://gogs@workhorse.private:2222/palo/plops.git"
"git@github.com:mrVanDalo/plops.git")
(sync-to-github "sync-image-generator"
"ssh://gogs@workhorse.private:2222/palo/image-generator2.git"
"git@github.com:mrVanDalo/image-generator.git")
(sync-to-github "sync-image-generator-lib"
"ssh://gogs@workhorse.private:2222/palo/image-generator-lib.git"
"git@github.com:mrVanDalo/image-generator-examples.git")
(sync-to-github "sync-tech.ingolf-wagner.de"
"ssh://gogs@workhorse.private:2222/palo/tech.ingolf-wagner.de.git"
"git@github.com:mrVanDalo/tech.ingolf-wagner.de.git")
(sync-to-github "sync-LineageOS-build"
"ssh://gogs@git.ingolf-wagner.de:443/palo/LineagoOS-build.git"
"git@github.com:mrVanDalo/LineagoOS-build.git")
(sync-to-github "sync-http-errors"
"ssh://gogs@git.ingolf-wagner.de:443/palo/http-errors.git"
"git@github.com:mrVanDalo/http-errors.git")
(sync-to-github "sync-light-control"
"ssh://gogs@git.ingolf-wagner.de:443/palo/light-control.git"
"git@github.com:mrVanDalo/light-control.git")
];
};
};
}

View File

@ -1,135 +0,0 @@
{ pkgs, lib, ... }:
let
hostAddress = "192.168.100.20";
containerAddress = "192.168.100.21";
in {
# backup mattermost
backup.dirs = [ "/home/mattermost" ];
containers.mattermost = {
# mount host folders
bindMounts = {
home = {
# make sure this folder exist on the host
hostPath = toString "/home/mattermost/home";
mountPoint = "/var/lib/mattermost";
isReadOnly = false;
};
db = {
# make sure this folder exist on the host
hostPath = toString "/home/mattermost/db";
mountPoint = "/var/lib/postgresql";
isReadOnly = false;
};
};
# container network setup
# see also nating on host system.
privateNetwork = true;
hostAddress = hostAddress;
localAddress = containerAddress;
autoStart = true;
config = { config, pkgs, lib, ... }: {
imports = [ <modules> <krops-lib> ];
services.nginx = {
# Use recommended settings
recommendedGzipSettings = lib.mkDefault true;
recommendedOptimisation = lib.mkDefault true;
recommendedProxySettings = lib.mkDefault true;
recommendedTlsSettings = lib.mkDefault true;
# for graylog logging
commonHttpConfig = let
access_log_sink = "${hostAddress}:12304";
error_log_sink = "${hostAddress}:12305";
in ''
log_format graylog2_json escape=json '{ "timestamp": "$time_iso8601", '
'"facility": "nginx", '
'"src_addr": "$remote_addr", '
'"body_bytes_sent": $body_bytes_sent, '
'"request_time": $request_time, '
'"response_status": $status, '
'"request": "$request", '
'"request_method": "$request_method", '
'"host": "$host",'
'"upstream_cache_status": "$upstream_cache_status",'
'"upstream_addr": "$upstream_addr",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"http_referrer": "$http_referer", '
'"http_user_agent": "$http_user_agent" }';
access_log syslog:server=${access_log_sink} graylog2_json;
error_log syslog:server=${error_log_sink};
'';
};
networking.firewall.allowedTCPPorts = [ 8065 6667 ];
networking.firewall.allowedUDPPorts = [ 8065 ];
# setup matter most
services.mattermost = {
enable = true;
siteUrl = "https://chat.ingolf-wagner.de";
localDatabaseName = "chat";
localDatabaseUser = "chatty";
listenAddress = ":8065";
matterircd = {
enable = true;
parameters = [
"-mmserver chat.ingolf-wagner.de"
"-restrict chat.ingolf-wagner.de"
"-bind [::]:6667"
];
};
};
# send log to host systems graylog (use tinc or wireguard if host is not graylog)
services.SystemdJournal2Gelf.enable = true;
services.SystemdJournal2Gelf.graylogServer = "${hostAddress}:11201";
services.journald.extraConfig = "SystemMaxUse=1G";
};
};
# give containers internet access
networking.nat.enable = true;
networking.nat.internalInterfaces = [ "ve-mattermost" ];
networking.nat.externalInterface = "enp2s0f1";
# don't let networkmanager manger container network
networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
# open ports for logging
networking.firewall.interfaces."ve-mattermost".allowedTCPPorts =
[ 11201 12304 12305 ];
networking.firewall.interfaces."ve-mattermost".allowedUDPPorts =
[ 11201 12304 12305 ];
# host nginx setup
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"chat.workhorse.private" = {
serverAliases = [ "chat.ingolf-wagner.de" ];
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://${containerAddress}:8065";
};
};
};
};
}

View File

@ -1,3 +0,0 @@
{ config, pkgs, ... }:
let unstable = import <nixpkgs-unstable> { };
in { environment.systemPackages = with pkgs; [ ]; }

77
flake.lock Normal file
View File

@ -0,0 +1,77 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1597053966,
"narHash": "sha256-f9lbPS/GJ1His8fsDqM6gfa8kSqREU4eKiMCS5hrKg4=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ec20f52e2ff61e9c36c2b894b62fc1b4bd04c71b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"krops": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1632420452,
"narHash": "sha256-ncK6vABW/Ku9XI0kqj1otarUfblryoQzSaOCnaZ0oSs=",
"owner": "Mic92",
"repo": "krops",
"rev": "0388970c568905fedcbf429e5745aacd4f7a6633",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "krops",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1635070614,
"narHash": "sha256-eRup9WsvSIhsRrSlNugPcQ7gfGOsbk3d4izufwVlz1Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3b1789322fcbcb5cf51228d732752714f1bf77da",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-21.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"krops": "krops",
"nixpkgs": "nixpkgs",
"secrets": "secrets"
}
},
"secrets": {
"flake": false,
"locked": {
"narHash": "sha256-tsXsKNsa6/AqhXV6YxsSweX++YlwzQuWt0KeaV3SMgQ=",
"path": "/home/palo/dev/secrets",
"type": "path"
},
"original": {
"path": "/home/palo/dev/secrets",
"type": "path"
}
}
},
"root": "root",
"version": 7
}

31
flake.nix Normal file
View File

@ -0,0 +1,31 @@
{
description = "my krops file";
inputs = {
secrets = {
url = "path:/home/palo/dev/secrets";
flake = false;
};
nixpkgs.url = "github:nixos/nixpkgs/nixos-21.05";
krops = {
url = "github:Mic92/krops";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, krops, secrets, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
writeCommand = krops.packages.${system}.writeCommand;
in
{
# deploy like this:
# nix run ".#deploy.sterni"
apps.${system}.deploy = pkgs.callPackage ./nixos/krops.nix {
inherit writeCommand secrets;
lib = krops.lib;
};
};
}

View File

@ -1,150 +0,0 @@
{ lib, ... }:
with builtins;
# https://jenkinsci.github.io/job-dsl-plugin/
{
# source container url and credentialsId
job = name:
{ url, credentialsId, branch ? "master",
# https://docs.openstack.org/infra/jenkins-job-builder/triggers.html
triggers ? [{
pollscm = {
cron = "H/30 * * * *";
ignore-post-commit-hooks = true;
};
}], ... }:
config: {
job = {
inherit name triggers;
sandbox = true;
project-type = "pipeline";
dsl = let
stage = elem:
let
stageName = head (attrNames elem);
stateScripts = map (stage:
lib.getAttr (typeOf stage) {
string = ''
withEnv(['PATH=/run/current-system/sw/bin/','NIX_PATH=/var/src/']) {
sh '${toString stage}'
}'';
set = let
script = ''
withEnv(['PATH=/run/current-system/sw/bin/','NIX_PATH=/var/src/']) {
sh '${toString stage.script}'
}
'';
in if (stage.credentialsId != null) then ''
sshagent(['${stage.credentialsId}']) { ${script} }
'' else
script;
}) (getAttr stageName elem);
in ''
stage('${stageName}') {
steps {
${concatStringsSep "\n" stateScripts}
}
}
'';
stages = map stage config;
in ''
pipeline {
agent any
post {
failure {
mattermostSend channel: 'notification', color: '#FF0000', message: "Failed to build : [''${env.JOB_NAME}-''${env.BUILD_NUMBER}](''${env.BUILD_URL})"
}
success {
mattermostSend channel: 'jenkins', color: '#00FF00', message: "Successfully build : [''${env.JOB_NAME}-''${env.BUILD_NUMBER}](''${env.JOB_URL})"
}
}
stages{
stage('Pull') {
steps {
checkout(
[$class: 'GitSCM'
, branches: [[name: '*/${branch}']]
, doGenerateSubmoduleConfigurations: false
, extensions: [[$class: 'LocalBranch', localBranch: 'master']]
, submoduleCfg: []
, userRemoteConfigs:
[[ credentialsId: '${credentialsId}'
, url: '${url}']]
]
)
}
}
${concatStringsSep "\n" stages}
}
}
'';
};
};
# creates a sync job
# source and target container url and credentialsId
syncJob = name: source: target: {
job = {
name = name;
sandbox = true;
project-type = "pipeline";
triggers = [{
pollscm = {
cron = "H/30 * * * *";
ignore-post-commit-hooks = true;
};
}];
dsl = ''
pipeline {
agent any
post {
failure {
mattermostSend channel: 'notification', color: '#FF0000', message: "Failed to build : [''${env.JOB_NAME}-''${env.BUILD_NUMBER}](''${env.BUILD_URL})"
}
success {
mattermostSend channel: 'jenkins', color: '#00FF00', message: "Successfully build : [''${env.JOB_NAME}-''${env.BUILD_NUMBER}](''${env.JOB_URL})"
}
}
stages{
stage('Pull') {
steps {
checkout(
[$class: 'GitSCM'
, branches: [[name: '*/master']]
, doGenerateSubmoduleConfigurations: false
, extensions: [[$class: 'LocalBranch', localBranch: 'master']]
, submoduleCfg: []
, userRemoteConfigs:
[[ credentialsId: '${source.credentialsId}'
, url: '${source.url}']]
]
)
}
}
stage('Push') {
steps {
sshagent(['${target.credentialsId}']) {
withEnv(['PATH=/run/current-system/sw/bin/','NIX_PATH=/var/src/']) {
sh "git push -f ${target.url}"
}
}
}
}
stage('Push Tags') {
steps {
sshagent(['${target.credentialsId}']) {
withEnv(['PATH=/run/current-system/sw/bin/']) {
sh "git push -f ${target.url} --tags"
}
}
}
}
}
}
'';
};
};
}

View File

@ -1,56 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.programs.custom.video;
# show keyboard input on desktop for screencasts
screenKey = pkgs.symlinkJoin {
name = "screen-keys";
paths = let
screenKeyScript = { position ? "bottom", size ? "small", ... }:
pkgs.writeShellScriptBin "screenkeys-${position}-${size}" # sh
''
${pkgs.screenkey}/bin/screenkey \
--no-detach \
--bg-color '#fdf6e3' \
--font-color '#073642' \
-p ${position} \
-s ${size} \
"$@"
'';
in lib.flatten (lib.flip map [ "large" "small" "medium" ] (size:
lib.flip map [ "top" "center" "bottom" ]
(position: screenKeyScript { inherit size position; })));
};
in {
options.programs.custom.video.enable = mkEnableOption "enable video tools";
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [
youtube-dl
mplayer
mpv
# to record your screen
# ---------------------
simplescreenrecorder
screenKey
# to transcode video material
# ---------------------------
handbrake
ffmpeg-full
# video editing
# -------------
openshot-qt
];
};
}

View File

@ -1,399 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.programs.custom.vim;
nix-xptemplates = pkgs.writeTextFile {
name = "nix-xptemplates";
destination = "/ftplugin/nix/nix.xpt.vim";
text = # vim
''
XPTemplate priority=personal
XPT option " tips
`name^ = mkOption {
type = with types; `type^;
description = ${"''"}
`cursor^
${"''"};
};
XPT package " tips
{ config, lib, ... }:
{
`cursor^
}
XPT terranix" tips
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.`name^;
in {
options.`name^ = mkOption {
default = {};
type = with types; attrsOf (submodule ({ name, ... }:{
options = {
enable = mkEnableOption "`name^.name";
};
}));
};
config =
let
allConfigs = cfg
in
mkIf (cfg != {} ){
`cursor^
};
}
XPT module " tips
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.`name^;
in {
options.`name^ = {
enable = mkEnableOption "enable `name^";
};
config = mkIf cfg.enable {
`cursor^
};
}
XPT shell " tips
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
# needed pkgs
# -----------
buildInputs = with pkgs; [
`name^
];
# run this on start
# -----------------
shellHook = ${"''"}
HISTFILE=${"$"}{toString ./.}/.history
${"''"};
}
XPT fhsUser " tips
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSUserEnv {
name = "fhs-user-env";
targetPkgs = pkgs: with pkgs; [
# core stuff
# ----------
vim silver-searcher curl coreutils git tig
# common X dependencies
# ---------------------
atk cairo dbus eudev expat fontconfig freetype gdk_pixbuf glib gnome3.GConf gtk2-x11
mesa_glu nspr nss pango xlibs.libXScrnSaver xlibs.libXcomposite xlibs.libXcursor
xlibs.libXdamage xlibs.libXfixes xlibs.libXi xlibs.libXrender xlibs.libXtst xorg.libX11
xorg.libXext xorg.libXinerama xorg.libxcb
liblo zlib fftw minixml libcxx alsaLib glibc
# new stuff
# ---------
`cursor^
];
# multilib packages
# -----------------
# these are packages compiled 32bit and 64bit
multiPkgs = pkgs: with pkgs; [
];
# environment variables
# ---------------------
profile = ${"''"}
export TERM="xterm"
${"''"};
}).env
'';
};
vim-tv-plugin = with lib;
((rtp: rtp // { inherit rtp; }) (pkgs.write "vim-tv" {
"/syntax/haskell.vim".text = # vim
''
syn region String start=+\[[[:alnum:]]*|+ end=+|]+
hi link ConId Identifier
hi link VarId Identifier
hi link hsDelimiter Delimiter
'';
"/syntax/nix.vim".text = # vim
''
"" Quit when a (custom) syntax file was already loaded
"if exists("b:current_syntax")
" finish
"endif
"setf nix
" Ref <nix/src/libexpr/lexer.l>
syn match NixID /[a-zA-Z\_][a-zA-Z0-9\_\'\-]*/
syn match NixINT /\<[0-9]\+\>/
syn match NixPATH /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
syn match NixHPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
syn match NixSPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
syn match NixURI /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
syn region NixSTRING
\ matchgroup=NixSTRING
\ start='"'
\ skip='\\"'
\ end='"'
syn region NixIND_STRING
\ matchgroup=NixIND_STRING
\ start="'''"
\ skip="'''\('\|[$]\|\\[nrt]\)"
\ end="'''"
syn match NixOther /[-!+&<>|():/;=.,?\[\]*@]/
syn match NixCommentMatch /\(^\|\s\)#.*/
syn region NixCommentRegion start="/\*" end="\*/"
hi link NixCode Statement
hi link NixData Constant
hi link NixComment Comment
hi link NixCommentMatch NixComment
hi link NixCommentRegion NixComment
hi link NixID NixCode
hi link NixINT NixData
hi link NixPATH NixData
hi link NixHPATH NixData
hi link NixSPATH NixData
hi link NixURI NixData
hi link NixSTRING NixData
hi link NixIND_STRING NixData
hi link NixEnter NixCode
hi link NixOther NixCode
hi link NixQuote NixData
syn cluster nix_has_dollar_curly contains=@nix_ind_strings,@nix_strings
syn cluster nix_ind_strings contains=NixIND_STRING
syn cluster nix_strings contains=NixSTRING
${concatStringsSep "\n" (mapAttrsToList (name:
{ extraStart ? null, lang ? name }:
let
startAlts = filter isString [ "/\\* ${name} \\*/" extraStart ];
sigil = "\\(${concatStringsSep "\\|" startAlts}\\)[ \\t\\r\\n]*";
# vim
in ''
syn include @nix_${lang}_syntax syntax/${lang}.vim
if exists("b:current_syntax")
unlet b:current_syntax
endif
syn match nix_${lang}_sigil
\ X${replaceStrings [ "X" ] [ "\\X" ] sigil}\ze\('''\|"\)X
\ nextgroup=nix_${lang}_region_IND_STRING,nix_${lang}_region_STRING
\ transparent
syn region nix_${lang}_region_STRING
\ matchgroup=NixSTRING
\ start='"'
\ skip='\\"'
\ end='"'
\ contained
\ contains=@nix_${lang}_syntax
\ transparent
syn region nix_${lang}_region_IND_STRING
\ matchgroup=NixIND_STRING
\ start="'''"
\ skip="'''\('\|[$]\|\\[nrt]\)"
\ end="'''"
\ contained
\ contains=@nix_${lang}_syntax
\ transparent
syn cluster nix_ind_strings
\ add=nix_${lang}_region_IND_STRING
syn cluster nix_strings
\ add=nix_${lang}_region_STRING
" This is required because containedin isn't transitive.
syn cluster nix_has_dollar_curly
\ add=@nix_${lang}_syntax
'') {
c = { };
cabal = { };
diff = { };
haskell = { };
python = { };
lua = { };
sed.extraStart = ''writeSed[^ \t\r\n]*[ \t\r\n]*"[^"]*"'';
sh.extraStart = concatStringsSep "\\|" [
''
write\(A\|Ba\|Da\)sh[^ \t\r\n]*[ \t\r\n]*\("[^"]*"\|[a-z]\+\)''
"[a-z]*Phase[ \\t\\r\\n]*="
];
yaml = { };
vim.extraStart = ''
write[^ \t\r\n]*[ \t\r\n]*"\(\([^"]*\.\)\?vimrc\|[^"]*\.vim\)"'';
xdefaults = { };
})}
" Clear syntax that interferes with nixINSIDE_DOLLAR_CURLY.
syn clear shVarAssign
syn region nixINSIDE_DOLLAR_CURLY
\ matchgroup=NixEnter
\ start="[$]{"
\ end="}"
\ contains=TOP
\ containedin=@nix_has_dollar_curly
\ transparent
syn region nix_inside_curly
\ matchgroup=NixEnter
\ start="{"
\ end="}"
\ contains=TOP
\ containedin=nixINSIDE_DOLLAR_CURLY,nix_inside_curly
\ transparent
syn match NixQuote /'''\($\|\\.\)/he=s+2
\ containedin=@nix_ind_strings
\ contained
syn match NixQuote /'''\('\|\\.\)/he=s+1
\ containedin=@nix_ind_strings
\ contained
syn match NixQuote /\\./he=s+1
\ containedin=@nix_strings
\ contained
syn sync fromstart
let b:current_syntax = "nix"
set isk=@,48-57,_,192-255,-,'
'';
"/syntax/sed.vim".text = # vim
''
syn region sedBranch
\ matchgroup=sedFunction start="T"
\ matchgroup=sedSemicolon end=";\|$"
\ contains=sedWhitespace
'';
}));
# active plugins
# --------------
extra-runtimepath = with pkgs;
lib.concatMapStringsSep "," (pkg: "${pkg.rtp}") [
vimPlugins.Syntastic
vimPlugins.ack-vim
vimPlugins.airline
vimPlugins.vim-nix
vimPlugins.xptemplate
vim-tv-plugin
];
# the vimrc
# ---------
vimrc = pkgs.writeText "vimrc" ''
" turn on linenumbers
" to turn of :set nonumber
:set number
" show Trailing Whitespaces
:set list listchars=tab:»·,trail:
" Map leader is the key for shortcuts
nnoremap <SPACE> <Nop>
let mapleader = "\<Space>"
" move blocks of text in visual mode
" does not work correctly
vmap <up> xkP`[V`]
vmap <down> xp`[V`]
" search/grep case insensitive
:set ignorecase
" tabs should always be 2 spaces
set et ts=2 sts=2 sw=2
" installed vim-plugins
set runtimepath=${extra-runtimepath},$VIMRUNTIME,$HOME/.vim,${nix-xptemplates}
" syntax highlighting on
syntax on
" xptemplates
" -----------
" a plugin to insert snippets on demand
set nocompatible
filetype plugin on
" enable cursor cross
" -------------------
":hi CursorLine cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white
":hi CursorColumn cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white
:hi CursorLine cterm=NONE ctermbg=0 guibg=#073642
:hi CursorColumn cterm=NONE ctermbg=0 guibg=#073642
set cursorline
set cursorcolumn
" save view
" ---------
augroup AutoSaveFolds
autocmd!
autocmd BufWinLeave * mkview
autocmd BufWinEnter * silent loadview
augroup END
" some language stuff
" -------------------
:map <leader>s :setlocal spell spelllang=en
'';
in {
# no options
options.programs.custom.vim.enable = lib.mkEnableOption "vim";
config = lib.mkIf cfg.enable {
# create vimrc
# ------------
# and load it as config for vim
environment.variables.VIMINIT = ":so /etc/vimrc";
environment.etc.vimrc.source = vimrc;
# set vim to the default editor
# -----------------------------
programs.vim.defaultEditor = true;
# install vim
# -----------
environment.systemPackages = [ pkgs.vim ];
};
}

View File

@ -1,95 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.system.permown;
nameGenerator = path: "permown.${replaceStrings [ "/" ] [ "_" ] path}";
in {
options.system.permown = mkOption {
default = { };
type = with types;
attrsOf (submodule ({ config, ... }: {
options = {
directory-mode = mkOption {
default = "=rwx";
type = types.str;
};
file-mode = mkOption {
default = "=rw";
type = types.str;
};
group = mkOption {
apply = x: if x == null then "" else x;
default = null;
type = types.nullOr types.str;
};
owner = mkOption { type = types.str; };
path = mkOption {
default = config._module.args.name;
type = types.path;
};
umask = mkOption {
default = "0027";
type = types.str;
};
timer = mkOption {
default = "hourly";
type = types.str;
description =
"OnCalendar string on how frequent should this command run";
};
};
}));
};
config = let plans = lib.attrValues cfg;
in mkIf (plans != [ ]) {
system.activationScripts.permown = let
mkdir = { path, ... }: ''
${pkgs.coreutils}/bin/mkdir -p ${path}
'';
in concatMapStrings mkdir plans;
systemd.services = listToAttrs (flip map plans
({ path, directory-mode, file-mode, owner, group, umask, ... }: {
name = nameGenerator path;
value = {
environment = {
DIR_MODE = directory-mode;
FILE_MODE = file-mode;
OWNER_GROUP = "${owner}:${group}";
ROOT_PATH = path;
};
path = [ pkgs.coreutils pkgs.findutils pkgs.inotifyTools ];
serviceConfig = {
ExecStart = pkgs.writers.writeDash "permown" ''
set -efu
find "$ROOT_PATH" -exec chown -h "$OWNER_GROUP" {} +
find "$ROOT_PATH" -type d -exec chmod "$DIR_MODE" {} +
find "$ROOT_PATH" -type f -exec chmod "$FILE_MODE" {} +
'';
PrivateTmp = true;
Restart = "always";
RestartSec = 10;
UMask = umask;
};
wantedBy = [ "multi-user.target" ];
};
}));
systemd.timers = listToAttrs (flip map plans ({ path, timer, ... }: {
name = nameGenerator path;
value = {
wantedBy = [ "multi-user.target" ];
timerConfig.OnCalendar = timer;
};
}));
};
}

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/WCoJ9zSL85R1mNJHUGRofyigeg2+g4+bwWVysHxWroPMIpP2hJrMRPoP13SAOaLOjl6X112jjoQ2wpJ/qptjtojrsF8bpFgoMqCQFsQDD4zuG4V/AaIt0nAF4B5tDBGFN3Hj6vpbwVAidv+8Kr41r5JOG/8Z/UiJDGrIMab3kDwyOklrMPLWr7IBYC0O8Jwyz3lAl18ukMSEZvoPaJhPJyyqRhSagX59U7AQiNrnq18kzi7Pszy3e1d7x3vWSXemJGZaUJ+cFbl1LrvFHwUa55sSUVUVBRxgABc906YoiUcr31aw98zUX4W+2+AqDzIIquV5frIc/+nnfsmDrsnMl81cLglxuRxqib0AuSYqkNQimWrR61M7TaLvGZomMk8Vheew/QlxvHvhbHwnu7/tgNll2i+Zi1T7VZ5Hcy4quYDZQA7NDrvu0dEm+dTlOfuJJZdMLWws20ao8xtv8IxxCN31CBCbCSETpsSuvT7joHKGpJoOf3eilLLqOKjrbo5E6s6S1w1WRoZ6LuXQo2l5uvMVSzUZ+4CG+FX+Q73bpQ5SWUvz2o5HovX8RbcneuG7mfZMe80F5IyaqSmi0r+kFOqK4NKz/InHhSJjrFYJWl2PP+30MfsHx5NMOVhfKdRZje5oTds6L2o9+3vhiE7CmgZVR+RqMHMUtRrERODwPQ==

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDIZ8ezpjX1nI/Im+krulJNaVd1DZeFvcgX3x197DIbBgCsBjAW8WuV2tzmcrWSjWJ6lfmLlzplOLzhTNG763sbNQ5amfhglYTmQhruGcSXNkdsjOyoxK8SX3wH6Kv/Q0CpoyAYOB9fiJnUyWB9BXSI8VYMMRFj6+cPiZc6NhnnlxU+6uH3VvzV5hsAaT4mpXJJEidMqE9DpGFguOekOUO7HCVcZeQQjazl1u+NhbqRI3CsJjXBqiqAFSMzKOXhTk36xd2WLEmCaPnWuWf7imvR+T3JVPFTNGec3wMbT5nnMiK0/hbowyDQCbtnUVWja9ftVsOH55tLRSY16GnXngUzw+trxWOvX0wOFOhfSZ2k1WtjTlNtOEhne/V0a3bocel/7JuXBX3RvWAEVl1sWS5R9MG8aDB8S2fx8qZirbg0NZticAxcHtg0RyJRzH6DYrkINE6cUgK7qsrUtaY1W6Qj6Jp33Li8KHY2JElDvJLhAx8v7l6BaZkog/Z0raR8RSRefiDQZJ8qiqPXUJG1pQm4Mp8IGL5PAmi0AZg6QL2pkXC0pyg1xE4TdRjOeuV/vdVDX15xCsgOJjK7PGNoNm2JpYA8vaWMHG8Ujk4UBHolooKeuL3g0CcgzfyRMp/Dxlk1BhgRQ17VxWBDuKt3bWuTJIvmrvDuPB3vd+WYNNqskQ==

View File

@ -7,8 +7,8 @@
allowSubRepos = true;
authorizedKeys = [
# todo rename
(lib.fileContents <common_secrets/backup/ssh_rsa.pub>)
(lib.fileContents <assets/ssh/card_rsa.pub>)
(lib.fileContents ../../assets/ssh/borg_access.pub)
(lib.fileContents ../../assets/ssh/card_rsa.pub)
];
};
};

View File

@ -2,7 +2,7 @@
imports = [
<system/server>
../../system/server
./hardware-configuration.nix
#./home-assistant.nix
@ -13,10 +13,14 @@
#./lan.nix
./dms.nix
./borg.nix
./mpd.nix
./grocy.nix
./taskwarrior-pushover.nix
];
nixpkgs.config.permittedInsecurePackages = [ "homeassistant-0.114.4" ];
sops.defaultSopsFile = ../../secrets/pepe.yaml;
networking.hostName = "pepe";

View File

@ -11,7 +11,8 @@ let
rev = "2f5c44f017bdfd8abfe908d419ef26bac300f809";
sha256 = "0dxhk1ah6wwbsxyk4hd32rz7886w7r5gfy16485gjbvky1qsi8gd";
};
in {
in
{
# setup ftp
services.vsftpd = {
@ -39,9 +40,11 @@ in {
}
];
sops.secrets.ftp_password = { };
# create user
users.users.ftp-upload = {
passwordFile = toString <secrets/ftp/password>;
passwordFile = config.sops.secrets.ftp_password.path;
isNormalUser = true;
};

View File

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
services.grocy = {
enable = true;
settings = {
culture = "de";
currency = "EUR";
};
hostName = "grocy.pepe.private";
nginx.enableSSL = false;
};
backup.dirs = [ config.services.grocy.dataDir ];
}

View File

@ -1,32 +1,34 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{ modulesPath, config, lib, pkgs, ... }:
{
imports = [
<nixpkgs/nixos/modules/installer/scan/not-detected.nix>
"${modulesPath}/installer/scan/not-detected.nix"
(let mediaUUID = "29ebe5ba-7599-4dd3-99a3-37b9bf8e4d61";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
})
(
let mediaUUID = "29ebe5ba-7599-4dd3-99a3-37b9bf8e4d61";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
}
)
];
boot.initrd.availableKernelModules =

View File

@ -1,6 +1,4 @@
{ pkgs, config, lib, ... }:
let unstablePkgs = import <nixpkgs-unstable> { };
in {
{ pkgs, config, lib, ... }: {
imports = [
#./home-assistant/mpd.nix
@ -156,12 +154,13 @@ in {
{
alias = "reset everything when back home";
trigger = map (entity_id: {
platform = "state";
entity_id = entity_id;
from = "off";
to = "on";
}) [
trigger = map
(entity_id: {
platform = "state";
entity_id = entity_id;
from = "off";
to = "on";
}) [
"binary_sensor.motion_sensor_1"
"binary_sensor.motion_sensor_2"
"binary_sensor.motion_sensor_3"
@ -187,65 +186,67 @@ in {
];
group = let
create_room = { name, description }: {
"${name}" = {
name = "${description}";
group =
let
create_room = { name, description }: {
"${name}" = {
name = "${description}";
entities = [ ];
};
};
create_rooms = rooms:
lib.foldr (a: b: a // b) { } (map create_room rooms);
# rooms
# -----
in
(create_rooms [
{
name = "floor_room";
description = "Flur";
}
{
name = "bed_room";
description = "Schlafzimmer";
}
{
name = "living_room";
description = "Wohnzimmer";
}
{
name = "kitchen_room";
description = "Küche";
}
{
name = "bath_room";
description = "Klo";
}
]) // {
# overview
# --------
all_sensors = { name = "Alle Sensoren"; };
today = {
name = "Today";
entities = [ "input_select.scene" ];
};
# other stuff
# -----------
tv = { name = "TV"; };
all_lights = { name = "Alle Lampen"; };
unknown = {
name = "Not Used";
entities = [ ];
};
};
create_rooms = rooms:
lib.foldr (a: b: a // b) { } (map create_room rooms);
# rooms
# -----
in (create_rooms [
{
name = "floor_room";
description = "Flur";
}
{
name = "bed_room";
description = "Schlafzimmer";
}
{
name = "living_room";
description = "Wohnzimmer";
}
{
name = "kitchen_room";
description = "Küche";
}
{
name = "bath_room";
description = "Klo";
}
]) // {
# overview
# --------
all_sensors = { name = "Alle Sensoren"; };
today = {
name = "Today";
entities = [ "input_select.scene" ];
};
# other stuff
# -----------
tv = { name = "TV"; };
all_lights = { name = "Alle Lampen"; };
unknown = {
name = "Not Used";
entities = [ ];
};
};
};
services.home-assistant = {
enable = true;
package = unstablePkgs.home-assistant;
#package = unstablePkgs.home-assistant.override {
# python3 = unstablePkgs.python37;
package = pkgs.unstable.home-assistant;
#package = pkgs.unstable.home-assistant.override {
# python3 = pkgs.unstable.python37;
# extraPackages = python: [
# # todo : check which is still needed
# python.netdisco

View File

@ -5,7 +5,8 @@ let
folderPath = config.services.home-assistant.configDir;
filePath = "${folderPath}/${name}.json";
in {
in
{
services.homeAssistantConfig = {
sensor = [

View File

@ -0,0 +1,232 @@
{ pkgs, lib, config, ... }: {
services.mqtt.light-control.enable = true;
services.mqtt.light-control.loglevel = "debug";
services.mqtt.light-control.config = {
credentials = {
host = "tcp://localhost:1883";
user = "homeassistant";
password = "hallo";
};
scenes = [
{
name = "up-dark";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "half";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
}
{
name = "down";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "up-bright";
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "outside";
room_tracking_enabled = false;
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "night";
room_tracking_enabled = false;
brightness = 25;
ignored_sensors =
[ "zigbee2mqtt/motion_sensor_7" "zigbee2mqtt/door_sensor_4" ];
}
];
sensors =
let
door = { topic, room }: {
topic = topic;
key = "contact";
room = room;
invert_state = true;
delay = 90;
};
motion = { topic, room }: {
topic = topic;
key = "occupancy";
room = room;
delay = 60;
};
in
[
(motion {
topic = "zigbee2mqtt/motion_sensor_1";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_2";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_6";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_8";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_7";
room = "sleeping_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_5";
room = "kitchen";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_4";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_1";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_5";
room = "sleeping_room";
})
(door {
# house door
topic = "zigbee2mqtt/door_sensor_4";
room = "floor";
})
];
switches =
let
sonoff = { id, rooms, delay ? 0 }: {
topic = "stat/${id}/RESULT";
key = "POWER";
rooms = rooms;
delay = delay;
command = {
command = "{{state}}";
init_command = "(null)";
topic = "cmnd/${id}/POWER";
on = "ON";
off = "OFF";
};
};
light = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
command = ''{"state":"{{state}}","brightness":{{brightness}}}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
led = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
# Configure it once to the color you like
# {"state":"{{state}}","brightness":{{brightness}},"color":{"hex":"#FFFFFF},"color_temp":255","transition":0}
command = ''
{"state":"{{state}}","brightness":{{brightness}},"transition":0}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
in
[
(light {
topic = "zigbee2mqtt/light_2";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_4";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_5";
rooms = [ "storage_room" ];
})
(light {
topic = "zigbee2mqtt/light_7";
rooms = [ "sleeping_room" ];
})
(led {
topic = "zigbee2mqtt/led_1";
rooms = [ "office_room" ];
})
(led {
topic = "zigbee2mqtt/led_2";
rooms = [ "kitchen" ];
})
#(sonoff {
# id = "PAL01";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL03";
# rooms = [ "living_room" ];
#})
#(sonoff {
# id = "PAL04";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL06";
# rooms = [ "kitchen" ];
#})
## monitor and speakers
#(sonoff {
# id = "PAL07";
# rooms = [ "bed_room" ];
# delay = 180;
#})
#(sonoff {
# id = "PAL08";
# rooms = [ "bed_room" ];
# delay = 180;
#})
];
};
}

View File

@ -39,7 +39,8 @@ let
toSwitch = name: "switch.${name}";
in {
in
{
imports = [ ./mqtt.nix ];
@ -47,28 +48,32 @@ in {
# nicer names
# -----------
homeassistant.customize = lib.mapAttrs' (entity:
{ label, icon ? "mdi:power-plug-off", ... }: {
name = toSwitch entity;
value = {
friendly_name = label;
icon = icon;
};
}) sonoffSwitches;
homeassistant.customize = lib.mapAttrs'
(entity:
{ label, icon ? "mdi:power-plug-off", ... }: {
name = toSwitch entity;
value = {
friendly_name = label;
icon = icon;
};
})
sonoffSwitches;
# define switches
# ---------------
switch = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
command_topic = "cmnd/${lib.toUpper name}/POWER";
state_topic = "stat/${lib.toUpper name}/POWER";
payload_on = "ON";
payload_off = "OFF";
state_on = "ON";
state_off = "OFF";
}) sonoffSwitches;
switch = lib.mapAttrsToList
(name:
{ ... }: {
name = name;
platform = "mqtt";
command_topic = "cmnd/${lib.toUpper name}/POWER";
state_topic = "stat/${lib.toUpper name}/POWER";
payload_on = "ON";
payload_off = "OFF";
state_on = "ON";
state_off = "OFF";
})
sonoffSwitches;
# discover state on init
# ----------------------
@ -78,27 +83,34 @@ in {
platform = "homeassistant";
event = "start";
};
action = lib.mapAttrsToList (name:
{ ... }: {
service = "mqtt.publish";
data = {
topic = "cmnd/${lib.toUpper name}/power";
payload = "";
};
}) sonoffSwitches;
action = lib.mapAttrsToList
(name:
{ ... }: {
service = "mqtt.publish";
data = {
topic = "cmnd/${lib.toUpper name}/power";
payload = "";
};
})
sonoffSwitches;
}];
# append to groups
# ----------------
group = let
# sort lights into given groups.
sortedInGroups = let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "switch.${name}"; }) groups)
sonoffSwitches));
in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in sortedInGroups;
group =
let
# sort lights into given groups.
sortedInGroups =
let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList
(name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "switch.${name}"; }) groups)
sonoffSwitches));
in
lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in
sortedInGroups;
};
}

View File

@ -0,0 +1,182 @@
{ lib, config, pkgs, ... }:
let
folderPath = config.services.home-assistant.configDir;
# find symbols with
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=<keywords>&apikey=<api_key>
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
#
# example:
# --------
# stocks = [
# {
# symbol = "GOOGL";
# name = "google";
# friendly_name = "Google";
# currency = "$";
# # I own 50 and bought at a price of 1000
# own = {
# pieces = 50;
# price = 1000;
# };
# }
# ];
stocks = import <secrets/home-assistant/stocks>;
filePath = name: "${folderPath}/stock_${name}.json";
cleanup_list = list: lib.filter (entry: entry != { }) (lib.flatten list);
in
{
services.homeAssistantConfig = {
sensor = cleanup_list (map
({ name, currency, own ? { }, ... }: [
{
platform = "file";
name = "stock_${name}";
file_path = filePath name;
value_template = "{{ value_json.price}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change";
file_path = filePath name;
value_template = "{{ value_json.change}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change_percent";
file_path = filePath name;
value_template = "{{ value_json.change_percent}} %";
}
(lib.optionalAttrs (own != { }) {
platform = "file";
name = "stock_${name}_profit";
file_path = filePath name;
value_template = ''
{{ "{:,.2f}".format( value_json.price * ${toString own.pieces} - ${
toString (own.pieces * own.price)
} ) }} ${currency}'';
})
])
stocks);
homeassistant = {
whitelist_external_dirs = [ folderPath ];
customize = builtins.listToAttrs (cleanup_list (map
({ name, own ? { }, ... }: [
{
name = "sensor.stock_${name}";
value = {
icon = "mdi:cash-usd-outline";
friendly_name = "Price";
};
}
{
name = "sensor.stock_${name}_change";
value = {
icon = "mdi:radar";
friendly_name = "Difference";
};
}
{
name = "sensor.stock_${name}_change_percent";
value = {
icon = "mdi:radar";
friendly_name = "Percent";
};
}
(lib.optionalAttrs (own != { }) {
name = "sensor.stock_${name}_profit";
value = {
icon = "mdi:radar";
friendly_name = "Profit";
};
})
])
stocks));
};
group = (builtins.listToAttrs (map
({ name, friendly_name, own ? { }, ... }: {
name = "stock_${name}";
value = {
name = "${friendly_name} Aktie";
entities = [
"sensor.stock_${name}"
"sensor.stock_${name}_change"
"sensor.stock_${name}_change_percent"
] ++ (lib.optional (own != { }) "sensor.stock_${name}_profit");
};
})
stocks));
};
systemd.services =
let
pullService = { name, symbol, currency, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
before = [ "home-assistant.service" ];
wantedBy = [ "home-assistant.service" ];
serviceConfig = {
User = "hass";
Type = "oneshot";
};
description = "pull stock_${name} for homeassistant";
script = ''
SYMBOL="${symbol}"
CURRENCY="${currency}"
APIKEY=${
lib.fileContents <secrets/home-assistant/alphavantage/apikey>
}
${pkgs.curl}/bin/curl --location --silent \
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
| ${pkgs.jq}/bin/jq --compact-output \
'.["Global Quote"] |
{
price: .["05. price"] | tonumber,
currency: "'$CURRENCY'",
change_percent: .["10. change percent"] | .[0:-1] | tonumber,
change: .["09. change"] | tonumber,
last_date: .["07. latest trading day"],
}' \
>> ${filePath name}
# old and stupid
#${pkgs.curl}/bin/curl --location --silent \
#"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=$SYMBOL&interval=5min&apikey=$APIKEY" \
#| ${pkgs.jq}/bin/jq --compact-output \
# '.["Time Series (5min)"] | to_entries | [ .[]
# | { date : .key , value : .value["4. close"], currency: "'$CURRENCY'" } ]
# | sort_by(.date) | reverse | .[0]' \
'';
};
};
in
builtins.listToAttrs (map pullService stocks);
systemd.timers =
let
pullTimer = { name, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = "true";
};
};
};
in
builtins.listToAttrs (map pullTimer stocks);
}

View File

@ -2,7 +2,7 @@
let
holiday-range = month: dayA: dayB:
map (day: "${month}-${toString day}")
(map (lib.fixedWidthNumber 2) (lib.range dayA dayB));
(map (lib.fixedWidthNumber 2) (lib.range dayA dayB));
privateHolidays = import <secrets/home-assistant/holidays>;
# for example :
# holidays = lib.flatten [
@ -11,7 +11,8 @@ let
#];
holidays = lib.flatten (privateHolidays holiday-range);
in {
in
{
services.homeAssistantConfig = {
binary_sensor = [

View File

@ -2,8 +2,7 @@
# no need to set ZIGBEE2MQTT_DATA anymore
assert lib.versionOlder lib.version "21.03";
let unstable = import <nixpkgs-unstable> { };
in {
{
imports = [
./mqtt.nix
./zigbee2mqtt/service.nix
@ -23,7 +22,7 @@ in {
enable = true;
#package = pkgs.own_zigbee2mqtt;
#package = unstable.zigbee2mqtt;
package = unstable.zigbee2mqtt.overrideAttrs (old: rec {
package = pkgs.unstable.zigbee2mqtt.overrideAttrs (old: rec {
version = "1.18.1";
src = pkgs.fetchFromGitHub {
owner = "Koenkk";

View File

@ -0,0 +1,250 @@
{ pkgs, lib, ... }:
let
# we create 3 input_boolean which get toggled by the 3 types of buttons pressed.
# input_boolean.single_${name} : single click
# input_boolean.double_${name} : double click
# input_boolean.hold_${name} : hold
# if you override these input (via states) you have to create the input yourself
# https://www.zigbee2mqtt.io/devices/WXKG12LM.html
allDevices = {
"button_a1" = {
id = "0x00158d0002b04f65";
#groups = [ "living_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a2" = {
id = "0x00158d0002b04f09";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a3" = {
id = "0x00158d0002b00e04";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
};
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
})
allDevices;
services.homeAssistantConfig = {
# define input_boolean
# --------------------
# which get toggled by the buttons
input_boolean =
let stripEmpty = lib.filter (a: a != { });
in builtins.listToAttrs (stripEmpty (lib.flatten (lib.mapAttrsToList
(name:
{ states ? { }, ... }: [
(lib.optionalAttrs (!lib.hasAttr "single" states) {
name = "single_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "double" states) {
name = "double_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "hold" states) {
name = "hold_${name}";
value = { icon = "mdi:toggle-switch"; };
})
])
allDevices)));
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
platform = "mqtt";
name = name;
icon = "mdi:toggle-switch";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.click }}";
}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
])
allDevices);
binary_sensor = lib.mapAttrsToList
(name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "motion";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = true;
payload_off = false;
value_template = "{{ value_json.occupancy }}";
})
allDevices;
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { states ? { }, ... }:
# let
# entityIds = { single ? "input_boolean.single_${name}"
# , double ? "input_boolean.double_${name}"
# , hold ? "input_boolean.hold_${name}", ... }: [
# single
# double
# hold
# ];
# in {
# entities = [ "sensor.${name}" ] ++ (entityIds states)
# ++ [ "sensor.battery_${name}" "sensor.link_${name}" ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], states ? { }, ... }:
# map (groupName: {
# "${groupName}" = if (lib.hasAttr "single" states) then
# states.single
# else
# "input_boolean.single_${name}";
# }) groups) allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
#};
# create automation
# -----------------
automation =
let
# single click
toggle_single_button_input = lib.mapAttrsToList
(name:
{ states ? { }, ... }:
let
entityId =
if (lib.hasAttr "single" states) then
states.single
else
"input_boolean.single_${name}";
in
{
alias = "toggle single click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "single" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
})
allDevices;
# double click
toggle_double_button_input = lib.mapAttrsToList
(name:
{ states ? { }, ... }:
let
entityId =
if (lib.hasAttr "double" states) then
states.double
else
"input_boolean.double_${name}";
in
{
alias = "toggle double click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "double" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
})
allDevices;
# hold
toggle_hold_button_input = lib.mapAttrsToList
(name:
{ states ? { }, ... }:
let
entityId =
if (lib.hasAttr "hold" states) then
states.hold
else
"input_boolean.hold_${name}";
in
{
alias = "toggle hold ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "hold" == trigger.payload_json.action}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
})
allDevices;
in
lib.flatten (toggle_single_button_input ++ toggle_double_button_input
++ toggle_hold_button_input);
};
}

View File

@ -0,0 +1,101 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html
allDevices = {
"door_sensor_1" = { id = "0x00158d000312dc52"; };
"door_sensor_2" = { id = "0x00158d000316d5bf"; };
"door_sensor_3" = { id = "0x00158d0002f9516f"; };
"door_sensor_4" = { id = "0x00158d00031383b9"; };
"door_sensor_5" = { id = "0x00158d0003120d3e"; };
};
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
})
allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
])
allDevices);
binary_sensor = lib.mapAttrsToList
(name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "door";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = false;
payload_off = true;
value_template = "{{ value_json.contact}}";
})
allDevices;
# create groups
# -------------
group =
let
# to have nice panels for every device
sensorGroups = lib.mapAttrs
(name:
{ ... }: {
entities = [
"binary_sensor.${name}"
"sensor.battery_${name}"
"sensor.link_${name}"
];
})
allDevices;
# sort lights into given groups.
sortedInGroups =
let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList
(name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "binary_sensor.${name}"; }) groups)
allDevices));
in
lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in
sortedInGroups // sensorGroups // {
all_sensors.entities =
lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
};
};
}

View File

@ -0,0 +1,57 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/E1757.html
allDevices = {
"office_fyrtur_1" = { id = "0x680ae2fffe64fa40"; };
"office_fyrtur_2" = { id = "0x680ae2fffe91d234"; };
"bedroom_fyrtur_1" = { id = "0x680ae2fffe6e9f41"; };
"broken_fyrtur_1" = { id = "0x680ae2fffe8f6411"; };
};
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":100}'
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":15}'
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 0.1;
};
})
allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
])
allDevices);
};
}

View File

@ -0,0 +1,85 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/SPZB0001.html
allDevices = {
"office_heater_1" = { id = "0x00158d00032f5ee4"; }; # office
"office_heater_2" = { id = "0x00158d00032f5f9f"; }; # office (kitchen)
"bedroom_heater_1" = { id = "0x00158d00032f6d1e"; }; # bed room
"storage_heater_1" = { id = "0x00158d00032f604d"; }; # abstell raum
};
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"auto","current_heating_setpoint":23}'
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"off"}'
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
legacy = false;
retain = false;
friendly_name = name;
transition = 1;
debounce = 0.5;
filtered_attributes = [
"battery_low"
"eurotronic_host_flags"
"eurotronic_system_mode"
#"occupied_heating_setpoint"
#"pi_heating_demand"
#"unoccupied_heating_setpoint"
];
};
})
allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
{
platform = "mqtt";
name = "temperature_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.local_temperature }}";
}
{
platform = "mqtt";
name = "pi_heating_demand_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
value_template = "{{ value_json.pi_heating_demand }}";
}
])
allDevices);
};
}

View File

@ -0,0 +1,48 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/GL-C-007-1ID.html
allDevices = {
"led_1" = { id = "0x00124b001f7a5be9"; };
"led_2" = { id = "0x00124b001ee958b3"; };
};
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","brightness":255,"color":{"hex":"#00FFFF"}}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF"}'
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 1;
};
})
allDevices;
services.homeAssistantConfig = {
light = lib.mapAttrsToList
(name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
})
allDevices;
};
}

View File

@ -13,32 +13,37 @@ let
"light_8" = { id = "0x7cb03eaa0a0384d3"; };
};
in {
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
osram_set_transition = 2; # time in seconds (integer or float)
};
}) allDevices;
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
osram_set_transition = 2; # time in seconds (integer or float)
};
})
allDevices;
services.homeAssistantConfig = {
light = lib.mapAttrsToList (name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
}) allDevices;
light = lib.mapAttrsToList
(name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
})
allDevices;
# sensor = with lib;
# mapAttrsToList (name:

View File

@ -0,0 +1,76 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/RTCGQ01LM.html
allDevices = {
"motion_sensor_1" = { id = "0x00158d0002fbd451"; };
"motion_sensor_2" = { id = "0x00158d0002f9a6b8"; };
"motion_sensor_3" = { id = "0x00158d0002f04522"; };
"motion_sensor_4" = { id = "0x00158d0002f9a558"; };
"motion_sensor_5" = { id = "0x00158d0002f9a56f"; };
"motion_sensor_6" = { id = "0x00158d0002f9a5cb"; };
"motion_sensor_7" = { id = "0x00158d0002f9a6aa"; };
"motion_sensor_8" = { id = "0x00158d0002f04637"; };
};
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, timeout ? 65, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
# should not be set below 60 seconds
occupancy_timeout = timeout;
};
})
allDevices;
services.homeAssistantConfig = {
# define meta information sensors
binary_sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [{
name = "${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.occupancy }}";
#icon = "mdi:battery-10";
payload_on = true;
payload_off = false;
device_class = "motion";
}])
allDevices);
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
])
allDevices);
};
}

View File

@ -9,10 +9,13 @@ let
"repeater4" = { id = "0x680ae2fffe8e2e71"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = { friendly_name = name; };
}) allDevices;
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = { friendly_name = name; };
})
allDevices;
}

View File

@ -26,7 +26,8 @@ let
# is copied from the store on startup
devices = "devices.yaml";
};
in {
in
{
options.custom.services.zigbee2mqtt = {
enable = mkEnableOption "enable zigbee2mqtt service";

View File

@ -0,0 +1,112 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/WSDCGQ11LM.html
allDevices = {
"temperature_sensor_1" = {
id = "0x00158d0002d79220";
groups = [ "living_room" ];
};
"temperature_sensor_2" = {
id = "0x00158d0002d7913d";
groups = [ "living_room" ];
};
};
in
{
services.zigbee2mqttConfiguration = lib.mapAttrs'
(name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
})
allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList
(name:
{ ... }: [
{
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.temperature }}";
}
{
platform = "mqtt";
name = "humidity_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
device_class = "humidity";
value_template = "{{ value_json.humidity }}";
}
#{
# platform = "mqtt";
# name = "pressure_${name}";
# state_topic = "zigbee2mqtt/${name}";
# availability_topic = "zigbee2mqtt/bridge/state";
# unit_of_measurement = "hPa";
# device_class = "pressure";
# value_template = "{{ value_json.pressure }}";
#}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
])
allDevices);
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { ... }: {
# entities = [
# "sensor.${name}"
# "sensor.humidity_${name}"
# #"sensor.pressure_${name}"
# "sensor.battery_${name}"
# "sensor.link_${name}"
# ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], ... }:
# map (groupName: { "${groupName}" = "sensor.${name}"; }) groups)
# allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "sensor.${name}") allDevices;
#};
};
}

View File

@ -5,7 +5,8 @@ let
ipAddress = "10.1.0.2";
prefixLength = 24;
in {
in
{
networking.extraHosts = ''
10.1.0.1 workout.lan

View File

@ -0,0 +1,92 @@
{ config, lib, pkgs, ... }:
{
services.mpd = {
enable = true;
network.listenAddress = "any";
musicDirectory = "/media/syncthing/music-library";
playlistDirectory = "/media/syncthing/music-library/playlists";
};
users.groups."syncthing".members = [ "mpd" ];
sound.enable = true;
networking.firewall.allowedTCPPorts = [
6680 # mopidy
6600 # mpd
1234 # zeroconf
];
users.users."spotifyd" = {
isSystemUser = true;
};
sops.secrets.spotify_pass = {
owner = "spotifyd";
};
sops.secrets.spotify_user = {
owner = "spotifyd";
};
services.spotifyd.enable = true;
services.spotifyd.config = ''
[global]
username_cmd = "cat ${config.sops.secrets.spotify_user.path}"
password_cmd = "cat ${config.sops.secrets.spotify_pass.path}"
backend = "alsa" # use portaudio for macOS [homebrew]
# The alsa audio device to stream audio to. To get a
# list of valid devices, run `aplay -L`,
#device = "alsa_audio_device" # omit for macOS
# The alsa mixer used by `spotifyd`.
mixer = "PCM" # omit for macOS
# A script that gets evaluated in the user's shell when the song changes [aliases: onevent]
on-song-change-hook = "${pkgs.mpc_cli}/bin/mpc --host localhost --port 6600 stop"
# The volume controller. Each one behaves different to
# volume increases. For possible values, run
# `spotifyd --help`.
volume_controller = "alsa" # use softvol for macOS
# The name that gets displayed under the connect tab on
# official clients. Spaces are not allowed!
device_name = "DJane"
# The audio bitrate. 96, 160 or 320 kbit/s
bitrate = 320
# The directory used to cache audio data. This setting can save
# a lot of bandwidth when activated, as it will avoid re-downloading
# audio files when replaying them.
#
# Note: The file path does not get expanded. Environment variables and
# shell placeholders like $HOME or ~ don't work!
#cache_path = "cache_directory"
# If set to true, audio data does NOT get cached.
no_audio_cache = true
# Volume on startup between 0 and 100
# NOTE: This variable's type will change in v0.4, to a number (instead of string)
initial_volume = "90"
# If set to true, enables volume normalisation between songs.
volume_normalisation = false
# The normalisation pregain that is applied for each song.
# normalisation_pregain = -10
# The port `spotifyd` uses to announce its service over the network.
zeroconf_port = 1234
# The proxy `spotifyd` will use to connect to spotify.
#proxy = "http://proxy.example.org:8080"
# The displayed device type in Spotify clients.
# Can be unknown, computer, tablet, smartphone, speaker, t_v,
# a_v_r (Audio/Video Receiver), s_t_b (Set-Top Box), and audio_dongle.
device_type = "computer"
'';
}

View File

@ -17,12 +17,15 @@
#};
};
sops.secrets.syncthing_cert = { };
sops.secrets.syncthing_key = { };
services.syncthing = {
enable = true;
openDefaultPorts = true;
declarative = {
cert = toString <secrets/syncthing/cert.pem>;
key = toString <secrets/syncthing/key.pem>;
cert = toString config.sops.secrets.syncthing_cert.path;
key = toString config.sops.secrets.syncthing_key.path;
overrideFolders = true;
folders = {

View File

@ -0,0 +1,24 @@
{ config, lib, pkgs, ... }:
{
users.users."taskwarrior-pushover".isSystemUser = true;
sops.secrets.pushoverApiToken.owner = "taskwarrior-pushover";
sops.secrets.pushoverUserKey.owner = "taskwarrior-pushover";
sops.secrets.taskwarriorCa.owner = "taskwarrior-pushover";
sops.secrets.taskwarriorCertificate.owner = "taskwarrior-pushover";
sops.secrets.taskwarriorKey.owner = "taskwarrior-pushover";
services.taskwarrior-pushover = {
enable = true;
recurrence = "on";
onCalendar = "06:30:00";
server = "taskd.ingolf-wagner.de:53589";
pushoverApiTokenFile = config.sops.secrets.pushoverApiToken.path;
pushoverUserKeyFile = config.sops.secrets.pushoverUserKey.path;
caFile = config.sops.secrets.taskwarriorCa.path;
certificateFile = config.sops.secrets.taskwarriorCertificate.path;
keyFile = config.sops.secrets.taskwarriorKey.path;
credentials = "1337/palo/ed0fdbe8-2dc3-408b-84cb-d07d363bccd2";
};
}

View File

@ -15,4 +15,7 @@ with lib;
};
};
sops.secrets.tinc_retiolum_ed25519_key = { };
sops.secrets.tinc_retiolum_rsa_key = { };
}

View File

@ -10,7 +10,8 @@ let
ssid = "palosiot";
wifiPassword = lib.fileContents <secrets/iot_wifi>;
in {
in
{
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];

View File

@ -27,26 +27,28 @@
# automount
# ---------
(let mediaUUID = "3d106f56-89e5-400d-9d6b-1dd957919548";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
})
(
let mediaUUID = "3d106f56-89e5-400d-9d6b-1dd957919548";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
}
)
];
# NTFS support

View File

@ -54,7 +54,12 @@
after = [ "media.mount" ];
};
users.groups."syncthing".members = [ "mpd" "syncthing" "kodi" "palo" ];
users.groups."syncthing".members = [
"mpd"
"syncthing"
"kodi"
"palo"
];
backup.dirs = [ "/var/lib/syncthing/finance" ];

View File

@ -8,7 +8,8 @@ let
ssid = "palosiot";
wifiPassword = lib.fileContents <secrets/iot_wifi>;
in {
in
{
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];

View File

@ -1,4 +1,2 @@
{ config, lib, ... }:
{
}
{ }

View File

@ -1,25 +1,20 @@
{ config, pkgs, lib, ... }: {
imports = [
<system/proxy>
../../system/proxy
./hardware-configuration.nix
<system/server/packages.nix>
../../system/server/packages.nix
./nginx.nix
./tinc.nix
./codimd.nix
./bitwarden.nix
#./syncplay.nix
./grocy.nix
];
nix = {
package = pkgs.nixUnstable;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
sops.defaultSopsFile = ../../secrets/sputnik.yaml;
networking.hostName = "sputnik";
networking.useDHCP = true;
@ -40,7 +35,7 @@
};
services.custom.ssh.sshd.rootKeyFiles =
[ (toString <secrets/ssh/jenkins_rsa.pub>) ];
[ ../../assets/ssh/jenkins.pub ];
# make sure ssh is only available trough the tinc
networking.firewall.extraCommands = ''

View File

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
services.grocy = {
enable = true;
settings = {
culture = "de";
currency = "EUR";
};
hostName = "grocy.ingolf-wagner.de";
nginx.enableSSL = true;
};
backup.dirs = [ config.services.grocy.dataDir ];
}

View File

@ -1,10 +1,11 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{ modulesPath, config, lib, pkgs, ... }:
{
imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ];
#imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ];
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
boot.initrd.availableKernelModules =
[ "ata_piix" "uhci_hcd" "virtio_pci" "sd_mod" "sr_mod" ];

View File

@ -3,7 +3,8 @@ let
domain = "io.ingolf-wagner.de";
publicIp = "195.201.134.247";
pw = import <secrets/iodinepw.nix>;
in {
in
{
services.iodine.server = {
enable = true;

View File

@ -1,6 +1,7 @@
{ config, lib, pkgs, ... }:
let
# todo create flake for this
errorPages = pkgs.fetchgit {
url = "https://git.ingolf-wagner.de/palo/http-errors.git";
rev = "74b8e4c1d9bbba3db6ad858b888e1867318af1f0";
@ -27,7 +28,8 @@ let
root = "${errorPages}/";
};
};
in {
in
{
networking.firewall.allowedTCPPorts =
[ 80 443 4443 config.services.taskserver.listenPort ];
@ -62,6 +64,7 @@ in {
};
};
"git.ingolf-wagner.de" = {
listen = [
{
@ -115,6 +118,21 @@ in {
} // error.locations;
};
"grocy.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
};
"paste.ingolf-wagner.de" = {
listen = [
{
@ -358,45 +376,6 @@ in {
} // error.locations;
};
#"home.ingolf-wagner.de" = {
# listen = [
# {
# addr = "0.0.0.0";
# port = 4443;
# ssl = true;
# }
# {
# addr = "0.0.0.0";
# port = 80;
# ssl = false;
# }
# ];
# extraConfig = ''
# proxy_buffering off;
# # client certificate
# ssl_client_certificate ${<secrets/client-cert/ca.crt>};
# # make verification optional, so we can display a 403 message to those
# # who fail authentication
# ssl_verify_client optional;
# '';
# forceSSL = true;
# enableACME = true;
# locations."/" = {
# proxyPass = "http://pepe.private:8123";
# proxyWebsockets = true;
# extraConfig = ''
# # if the client-side certificate failed to authenticate, show a 403
# # message to the client
# if ($ssl_client_verify != SUCCESS) {
# return 403;
# }
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection $connection_upgrade;
# '';
# };
#};
};
};
@ -461,10 +440,11 @@ in {
systemd.services."socat-taskd" = {
wantedBy = [ "multi-user.target" ];
script = let port = toString config.services.taskserver.listenPort;
in ''
${pkgs.socat}/bin/socat TCP-LISTEN:${port},fork TCP:workhorse.private:${port}
'';
script =
let port = toString config.services.taskserver.listenPort;
in ''
${pkgs.socat}/bin/socat TCP-LISTEN:${port},fork TCP:workhorse.private:${port}
'';
};
}

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