diff --git a/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg b/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg new file mode 100644 index 0000000..469ad36 Binary files /dev/null and b/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg differ diff --git a/.git-crypt/keys/default/0/66FB54F6081375106EEBF651A222365EB448F934.gpg b/.git-crypt/keys/default/0/66FB54F6081375106EEBF651A222365EB448F934.gpg deleted file mode 100644 index 8dcb496..0000000 Binary files a/.git-crypt/keys/default/0/66FB54F6081375106EEBF651A222365EB448F934.gpg and /dev/null differ diff --git a/.gitattributes b/.gitattributes index 32845f7..eea1737 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ **/secrets/** filter=git-crypt diff=git-crypt **/secrets.yaml diff=sops +*.wav filter=lfs diff=lfs merge=lfs -text + +hosts/iron/services/tvproxy.nix filter=git-crypt diff=git-crypt diff --git a/.sops.yaml b/.sops.yaml index c264349..44b3cd9 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,11 +1,12 @@ keys: - - &admin_jalr 66FB54F6081375106EEBF651A222365EB448F934 + - &admin_jalr 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 - &admin_jalr_tb FE170812543DF81393EA56BA5042B8317A10617E - &host_aluminium age1ne08hny30vrkejqhh7dcx4ql6dmkx6jw9dqkf3cz7mzvt53njy0qh59w44 - &host_hafnium age1ahnfjspcpwxxk7getcxkj3fypwt37rr6p3xsmp8n2tqqqz8jtg7q2am0et - &host_iron age1hx7fdu4mcha7kkxe7yevtvs6xgzgaafgenm3drhvr609wlj94sgqm497je - - &host_magnesium age1swv42gad884z2v75kateem6k2za6ltkq6wu90ewqp6dp7gxprawslwz0w0 + - &host_magnesium age19qkgfaq08kmyxghet48dq4gxwjuy9zpvuyxys9jkmcqa5634537qlxjcd8 - &host_weinturm_pretix_prod age1djjxl3lcvzs85nj0met6w8ujsz8pvr6ngmmdwlxfh0k9d5lkrpdqlzzehf + - &host_copper age1rrut5ntrkqmvttvmpa5jcmjhr2pfpyaqgu9dmtx6v07lgjxx5ppsl7e5v3 creation_rules: - path_regex: hosts/aluminium/secrets\.yaml$ key_groups: @@ -37,6 +38,12 @@ creation_rules: - *admin_jalr age: - *host_weinturm_pretix_prod + - path_regex: hosts/copper/secrets\.yaml$ + key_groups: + - pgp: + - *admin_jalr + age: + - *host_copper - path_regex: secrets\.yaml$ key_groups: - pgp: diff --git a/README.md b/README.md index 15ccbb8..332bf77 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,27 @@ -## home-manager -https://github.com/nix-community/home-manager - -For a systematic overview of Home Manager and its available options, please see -- the [Home Manager manual](https://nix-community.github.io/home-manager/index.html) and -- the [Home Manager configuration options](https://nix-community.github.io/home-manager/options.html). - +# jalr's NixOS Configuration ## Install a new host This installs nixos on host `somehost`: + +### NixOS Anywhere + +```bash +nix run github:nix-community/nixos-anywhere -- --flake .# root@ +``` + +### The traditional way + ```bash nix-shell -p nixUnstable --run 'nixos-install --flake https://gitlab.jalr.de/jalr/nixos-configuration#somehost --no-channel-copy' ``` +### Build a configuration + +``` +nix build .#nixosConfigurations.iron.config.system.build.toplevel +``` + ### setting up sops Get the host key and convert it. ```bash @@ -46,4 +55,12 @@ nix-repl> :lf .# ``` gpg --card-edit gpg/card> fetch +gpg --edit-key $key +gpg> trust +Your decision? 5 ``` + +## Debugging boot issues + +1. Add `rd.systemd.debug_shell` kernel parameter +2. Press CTRL+ALT+F9 to switch to root shell diff --git a/custom-utils/default.nix b/custom-utils/default.nix new file mode 100644 index 0000000..9bc4a53 --- /dev/null +++ b/custom-utils/default.nix @@ -0,0 +1,5 @@ +{ lib, ... }: + +{ + validatePortAttrset = import ./ports.nix { inherit lib; }; +} diff --git a/custom-utils/ports.nix b/custom-utils/ports.nix new file mode 100644 index 0000000..a8d1a54 --- /dev/null +++ b/custom-utils/ports.nix @@ -0,0 +1,33 @@ +{ lib, ... }: + +let + filterPort = pm: port: ( + lib.attrsets.catAttrs port ( + lib.attrsets.attrValues ( + lib.attrsets.filterAttrs (_: v: v ? "${port}") pm + ) + ) + ); + onlyUniqueItemsInList = x: lib.lists.length x == lib.lists.length (lib.lists.unique x); + mkRange = { from, to }: (lib.lists.range from to); +in +portmap: +if builtins.all + ( + proto: + if onlyUniqueItemsInList + ( + lib.flatten ( + map + (x: + if lib.isInt x then x + else if lib.isList x then x + else if lib.isAttrs x then mkRange x + else builtins.abort "found invalid entry in portmap" + ) + (filterPort portmap proto) + ) + ) then true else builtins.abort "Found duplicate ${proto} ports." + ) [ "tcp" "udp" ] +then portmap +else builtins.abort "Found duplicate ports." diff --git a/flake.lock b/flake.lock index 01cef87..7c92712 100644 --- a/flake.lock +++ b/flake.lock @@ -1,13 +1,91 @@ { "nodes": { + "asterisk-sounds-de": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nix-filter": [ + "nix-filter" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748284610, + "narHash": "sha256-B3/OOZC0puXbODupPEbdMA6sJP39MzbMCl4j1HvgNfU=", + "ref": "refs/heads/main", + "rev": "6b1c484318727af78a64aee3f46903493dae8259", + "revCount": 1, + "type": "git", + "url": "https://git.jalr.de/jalr/asterisk-sounds-de" + }, + "original": { + "type": "git", + "url": "https://git.jalr.de/jalr/asterisk-sounds-de" + } + }, + "bldcSrc": { + "flake": false, + "locked": { + "lastModified": 1733324381, + "narHash": "sha256-ui9N8QSog1G5zyK7yRrD0Xl+Y2CZhvvhBkaJuQZ2qZw=", + "owner": "vedderb", + "repo": "bldc", + "rev": "a0d40e2c5a42c810888d8c379307e6b0a118a125", + "type": "github" + }, + "original": { + "owner": "vedderb", + "ref": "release_6_05", + "repo": "bldc", + "type": "github" + } + }, + "crane": { + "locked": { + "lastModified": 1731098351, + "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762276996, + "narHash": "sha256-TtcPgPmp2f0FAnc+DMEw4ardEgv1SGNR3/WFGH0N19M=", + "owner": "nix-community", + "repo": "disko", + "rev": "af087d076d3860760b3323f6b583f4d828c1ac17", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -16,16 +94,74 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1687709756, - "narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -34,7 +170,49 @@ "type": "github" } }, + "gg-chatmix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748177977, + "narHash": "sha256-xC/dOrDrZoQhUfVotj/z14iTwGlE80OqSl9S5zkevdA=", + "owner": "nilathedragon", + "repo": "gg-chatmix", + "rev": "1dadaa51794042c20ddc52d52479e8a156bd235b", + "type": "github" + }, + "original": { + "owner": "nilathedragon", + "repo": "gg-chatmix", + "type": "github" + } + }, "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { "inputs": { "nixpkgs": [ "nix-pre-commit-hooks", @@ -42,11 +220,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -55,6 +233,29 @@ "type": "github" } }, + "gomod2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759991118, + "narHash": "sha256-pDyrtUQyeP1lVTMIYqJtftzDtsXEZaJjYy9ZQ/SGhL8=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "7f8d7438f5870eb167abaf2c39eea3d2302019d1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -62,20 +263,35 @@ ] }, "locked": { - "lastModified": 1687871164, - "narHash": "sha256-bBFlPthuYX322xOlpJvkjUBz0C+MOBjZdDOOJJ+G2jU=", + "lastModified": 1758463745, + "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", "owner": "nix-community", "repo": "home-manager", - "rev": "07c347bb50994691d7b0095f45ebd8838cf6bc38", + "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-23.05", + "ref": "release-25.05", "repo": "home-manager", "type": "github" } }, + "impermanence": { + "locked": { + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "krops": { "inputs": { "flake-utils": [ @@ -99,86 +315,146 @@ "type": "github" } }, - "nix-pre-commit-hooks": { + "lanzaboote": { "inputs": { + "crane": "crane", "flake-compat": "flake-compat", - "flake-utils": [ - "flake-utils" + "flake-parts": "flake-parts", + "nixpkgs": [ + "nixpkgs" ], - "gitignore": "gitignore", - "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable" + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1688596063, - "narHash": "sha256-9t7RxBiKWHygsqXtiNATTJt4lim/oSYZV3RG8OjDDng=", + "lastModified": 1737639419, + "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "v0.4.2", + "repo": "lanzaboote", + "type": "github" + } + }, + "nix-filter": { + "locked": { + "lastModified": 1757882181, + "narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "59c44d1909c72441144b93cf0f054be7fe764de5", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729742964, + "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix-pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat_2", + "gitignore": "gitignore_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1763319842, + "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=", "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "c8d18ba345730019c3faf412c96a045ade171895", + "repo": "git-hooks.nix", + "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761", "type": "github" }, "original": { "owner": "cachix", "ref": "master", - "repo": "pre-commit-hooks.nix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "nixos-hardware": { + "locked": { + "lastModified": 1762847253, + "narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", + "owner": "nixos", + "repo": "nixos-hardware", + "rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "master", + "repo": "nixos-hardware", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1685866647, - "narHash": "sha256-4jKguNHY/edLYImB+uL8jKPL/vpfOvMmSlLAGfxSrnY=", - "owner": "NixOS", + "lastModified": 1763334038, + "narHash": "sha256-LBVOyaH6NFzQ3X/c6vfMZ9k4SV2ofhpxeL9YnhHNJQQ=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "a53a3bec10deef6e1cc1caba5bc60f53b959b1e8", + "rev": "4c8cdd5b1a630e8f72c9dd9bf582b1afb3127d2c", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", + "owner": "nixos", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1685801374, - "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable_2": { - "locked": { - "lastModified": 1688868408, - "narHash": "sha256-RR9N5XTAxSBhK8MCvLq9uxfdkd7etC//seVXldy0k48=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "510d721ce097150ae3b80f84b04b13b039186571", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-23.05", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgsMaster": { "locked": { - "lastModified": 1689015649, - "narHash": "sha256-so1x2XDFiN6B9JO2rDmscgQ7+urdQRbnNRwiq50KJnM=", + "lastModified": 1763473525, + "narHash": "sha256-NzmsN8hRIn/9rJvZH3vPirBrOJJfeSfvPr4+feeK7LY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "72c08deeab9e6ca478aad5416e63b72bef5c11fb", + "rev": "15901670689a6f338ebd2a9436b947ec189463a3", "type": "github" }, "original": { @@ -188,29 +464,65 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgsOld": { "locked": { - "lastModified": 1688939073, - "narHash": "sha256-jYhYjeK5s6k8QS3i+ovq9VZqBJaWbxm7awTKNhHL9d0=", + "lastModified": 1748037224, + "narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8df7a67abaf8aefc8a2839e0b48f92fdcf69a38b", + "rev": "f09dede81861f3a83f7f06641ead34f02f37597f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-23.05", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1763283776, + "narHash": "sha256-Y7TDFPK4GlqrKrivOcsHG8xSGqQx3A6c+i7novT85Uk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "50a96edd8d0db6cc8db57dab6bb6d6ee1f3dc49a", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1735554305, + "narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nur": { + "inputs": { + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_2" + }, "locked": { - "lastModified": 1689007503, - "narHash": "sha256-T5oA+mq2ZXTVNWvCSvZqrjZBaVzY9hwwiI4uPiGkXM4=", + "lastModified": 1763471545, + "narHash": "sha256-B1ua1UtkPuMwT8o4nOR7yNP5yz10usMcNnxwHpGtLck=", "owner": "nix-community", "repo": "NUR", - "rev": "46663cf4a220139e81691144278fa1f637c22615", + "rev": "4c584dcedf9aa3394e9730e62693515a0e47674b", "type": "github" }, "original": { @@ -219,31 +531,114 @@ "type": "github" } }, + "poetry2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1743690424, + "narHash": "sha256-cX98bUuKuihOaRp8dNV1Mq7u6/CQZWTPth2IJPATBXc=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "ce2369db77f45688172384bbeb962bc6c2ea6f94", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1731363552, + "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { + "asterisk-sounds-de": "asterisk-sounds-de", + "disko": "disko", "flake-utils": "flake-utils", + "gg-chatmix": "gg-chatmix", + "gomod2nix": "gomod2nix", "home-manager": "home-manager", + "impermanence": "impermanence", "krops": "krops", + "lanzaboote": "lanzaboote", + "nix-filter": "nix-filter", "nix-pre-commit-hooks": "nix-pre-commit-hooks", - "nixpkgs": "nixpkgs_2", + "nixos-hardware": "nixos-hardware", + "nixpkgs": "nixpkgs", "nixpkgsMaster": "nixpkgsMaster", "nur": "nur", - "sops-nix": "sops-nix" + "poetry2nix": "poetry2nix", + "sops-nix": "sops-nix", + "vesc-tool": "vesc-tool" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731897198, + "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } }, "sops-nix": { "inputs": { "nixpkgs": [ "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable_2" + ] }, "locked": { - "lastModified": 1688873469, - "narHash": "sha256-9TMSXvXmrr7bDYi+WeskWe/yho9UP01dGbV9vW5bRVc=", + "lastModified": 1763417348, + "narHash": "sha256-n5xDOeNN+smocQp3EMIc11IzBlR9wvvTIJZeL0g33Fs=", "owner": "Mic92", "repo": "sops-nix", - "rev": "b2047c8fc963407916ad3834165309007dc5a1f7", + "rev": "3f66a7fb9626a9a9c077612ef10a0ce396286c7d", "type": "github" }, "original": { @@ -266,6 +661,87 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730120726, + "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "vesc-tool": { + "inputs": { + "bldcSrc": "bldcSrc", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgsOld": "nixpkgsOld", + "treefmt-nix": "treefmt-nix_2" + }, + "locked": { + "lastModified": 1762968599, + "narHash": "sha256-j+AZQYOuZ0X33p76LsZu4/NZl1Ccu6kkwPKC5HpIn1Y=", + "owner": "vedderb", + "repo": "vesc_tool", + "rev": "6a75051ce9742d97f14addd5d175ac516effb3c6", + "type": "github" + }, + "original": { + "owner": "vedderb", + "ref": "master", + "repo": "vesc_tool", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index a7e0939..d56e73d 100644 --- a/flake.nix +++ b/flake.nix @@ -1,19 +1,58 @@ { inputs = { - flake-utils.url = "github:numtide/flake-utils"; - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05"; - nixpkgsMaster.url = "github:NixOS/nixpkgs/master"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + disko.url = "github:nix-community/disko"; - nur.url = "github:nix-community/NUR"; + flake-utils.url = "github:numtide/flake-utils"; + + nix-filter.url = "github:numtide/nix-filter"; + + gg-chatmix = { + url = "github:nilathedragon/gg-chatmix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + gomod2nix = { + url = "github:nix-community/gomod2nix"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; home-manager = { - url = "github:nix-community/home-manager/release-23.05"; + url = "github:nix-community/home-manager/release-25.05"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + impermanence.url = "github:nix-community/impermanence"; + + krops = { + url = "github:Mic92/krops"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + lanzaboote = { + url = "github:nix-community/lanzaboote/v0.4.2"; inputs.nixpkgs.follows = "nixpkgs"; }; nix-pre-commit-hooks = { - url = "github:cachix/pre-commit-hooks.nix/master"; + url = "github:cachix/git-hooks.nix/master"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + nixos-hardware.url = "github:nixos/nixos-hardware/master"; + + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + + nixpkgsMaster.url = "github:NixOS/nixpkgs/master"; + + nur.url = "github:nix-community/NUR"; + + poetry2nix = { + url = "github:nix-community/poetry2nix"; inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; }; sops-nix = { @@ -21,22 +60,29 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - krops = { - url = "github:Mic92/krops"; + asterisk-sounds-de = { + url = "git+https://git.jalr.de/jalr/asterisk-sounds-de"; inputs = { flake-utils.follows = "flake-utils"; + nix-filter.follows = "nix-filter"; nixpkgs.follows = "nixpkgs"; }; }; + + vesc-tool = { + url = "github:vedderb/vesc_tool/master"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self - , nixpkgs , flake-utils - , krops , home-manager - , nur + , krops , nix-pre-commit-hooks + , nixpkgs + , nur , ... }@inputs: flake-utils.lib.eachSystem [ "x86_64-linux" @@ -52,20 +98,26 @@ src = self; hooks = { black.enable = true; + deadnix.enable = true; nixpkgs-fmt.enable = true; shellcheck.enable = true; + statix = { + enable = true; + settings.ignore = [ ".direnv" ]; + }; }; + excludes = [ ".envrc" ]; }; }; devShells.default = pkgs.mkShell { - buildInputs = (with pkgs; [ + buildInputs = with pkgs; [ black just nixpkgs-fmt shellcheck sops ssh-to-age - ]); + ]; shellHook = '' ${self.checks.${system}.pre-commit-check.shellHook} @@ -73,7 +125,7 @@ }; apps = lib.mapAttrs - (name: program: { type = "app"; program = toString program; }) + (_: program: { type = "app"; program = toString program; }) (flake-utils.lib.flattenTree { deploy = lib.recurseIntoAttrs (lib.mapAttrs (hostname: machine: @@ -97,6 +149,7 @@ command = targetPath: '' nixos-rebuild switch --flake ${targetPath}/config -L --keep-going ''; + force = true; } ) self.nixosConfigurations); @@ -124,46 +177,74 @@ }; }); }) // { - overlay = import ./pkgs; + overlays.default = import ./pkgs inputs; nixosConfigurations = nixpkgs.lib.mapAttrs (hostname: { system , extraModules ? [ ] , targetHost ? hostname , nixpkgs ? inputs.nixpkgs - }: nixpkgs.lib.nixosSystem rec { + }: nixpkgs.lib.nixosSystem { inherit system; specialArgs = { inherit self system; }; - modules = [ - (./hosts + "/${hostname}/configuration.nix") + modules = + let + hostDir = ./hosts + "/${hostname}"; + in + [ + (hostDir + "/configuration.nix") - ./modules + ./modules - { - _module.args.inputs = inputs; - } - - # deployment settings - ({ lib, ... }: { - options.deployment = { - targetHost = lib.mkOption { - type = lib.types.str; - readOnly = true; - internal = true; + { + _module.args = { + inherit inputs; + custom-utils = import ./custom-utils { inherit (nixpkgs) lib; }; }; - }; - config.deployment = { - inherit targetHost; - }; - }) - ] ++ [{ - nixpkgs.overlays = [ nur.overlay ]; - }] ++ [ - home-manager.nixosModules.home-manager - ] ++ (with inputs; [ - sops-nix.nixosModules.sops - ]) ++ extraModules; + } + + # deployment settings + ({ lib, ... }: { + options.deployment = { + targetHost = lib.mkOption { + type = lib.types.str; + readOnly = true; + internal = true; + }; + }; + config.deployment = { + inherit targetHost; + }; + }) + + # sops settings + ({ lib, config, pkgs, ... }: + { + sops.defaultSopsFile = hostDir + "/secrets.yaml"; + sops.secrets = + let + secretFile = config.sops.defaultSopsFile; + getSecrets = file: builtins.fromJSON (builtins.readFile (pkgs.runCommandNoCC "secretKeys" { } ''${pkgs.yq-go}/bin/yq -o json '[del .sops | .. | select(tag != "!!seq" and tag != "!!map") | path | join("/")]' ${file} > $out'')); + secretNames = getSecrets secretFile; + secrets = + if builtins.pathExists secretFile then + lib.listToAttrs (builtins.map (name: lib.nameValuePair name { }) secretNames) + else + { }; + in + secrets; + }) + ] ++ [ + { nixpkgs.overlays = [ nur.overlays.default inputs.vesc-tool.overlays.default ]; } + home-manager.nixosModules.home-manager + inputs.asterisk-sounds-de.nixosModules.default + inputs.disko.nixosModules.disko + inputs.impermanence.nixosModules.impermanence + inputs.lanzaboote.nixosModules.lanzaboote + inputs.sops-nix.nixosModules.sops + inputs.gg-chatmix.nixosModule + ] ++ extraModules; }) (import ./hosts inputs); }; diff --git a/home-manager/README.md b/home-manager/README.md deleted file mode 100644 index e245e64..0000000 --- a/home-manager/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Documentation -[Home Manager Manual](https://rycee.gitlab.io/home-manager/) diff --git a/home-manager/modules/alacritty.nix b/home-manager/modules/alacritty.nix deleted file mode 100644 index 6974d18..0000000 --- a/home-manager/modules/alacritty.nix +++ /dev/null @@ -1,174 +0,0 @@ -{ lib, pkgs, nixosConfig, ... }: -let - solarized = import ./solarized.nix; - - #nixosConfig.jalr.terminalEmulator.command = pkgs.writeShellScriptBin "alacritty-sway-cwd" '' - # this_alacritty_pid="$(swaymsg -t get_tree | ${pkgs.jq} -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="Alacritty")).pid')" - - # if [ "$this_alacritty_pid" ]; then - # child_pid="$(pgrep -P "$this_alacritty_pid")" - # cwd="$(readlink /proc/$child_pid/cwd)" - # fi - # if [ -e "$cwd" ]; then - # exec ${pkgs.alacritty} --working-directory "$cwd" - # fi - - # exec alacritty - #''; - - colorschemes = { - # https://github.com/alacritty/alacritty/wiki/Color-schemes#solarized - solarized-dark = { - # Default colors - primary = { - background = solarized.base03.hex; - foreground = solarized.base0.hex; - }; - - # Cursor colors - cursor = { - text = solarized.base03.hex; - cursor = solarized.base0.hex; - }; - - # Normal colors - normal = { - black = solarized.base02.hex; - red = solarized.red.hex; - green = solarized.green.hex; - yellow = solarized.yellow.hex; - blue = solarized.blue.hex; - magenta = solarized.magenta.hex; - cyan = solarized.cyan.hex; - white = solarized.base2.hex; - }; - - # Bright colors - bright = { - black = solarized.base03.hex; - red = solarized.orange.hex; - green = solarized.base01.hex; - yellow = solarized.base00.hex; - blue = solarized.base0.hex; - magenta = solarized.violet.hex; - cyan = solarized.base1.hex; - white = solarized.base3.hex; - }; - }; - - solarized-light = { - # Default colors - primary = { - background = solarized.base3.hex; - foreground = solarized.base00.hex; - }; - - # Cursor colors - cursor = { - text = solarized.base3.hex; - cursor = solarized.base00.hex; - }; - - # Normal colors - normal = { - black = solarized.base02.hex; - red = solarized.red.hex; - green = solarized.green.hex; - yellow = solarized.yellow.hex; - blue = solarized.blue.hex; - magenta = solarized.magenta.hex; - cyan = solarized.cyan.hex; - white = solarized.base2.hex; - }; - - # Bright colors - bright = { - black = solarized.base03.hex; - red = solarized.orange.hex; - green = solarized.base01.hex; - yellow = solarized.base00.hex; - blue = solarized.base0.hex; - magenta = solarized.violet.hex; - cyan = solarized.base1.hex; - white = solarized.base3.hex; - }; - }; - }; - commonSettings = { - font = { - normal = { - family = "Inconsolata for Powerline"; - style = "Regular"; - }; - size = 12; - }; - - mouse.hide_when_typing = true; - - key_bindings = [ - { - key = "F1"; - mods = "Control"; - action = "DecreaseFontSize"; - } - { - key = "F2"; - mods = "Control"; - action = "IncreaseFontSize"; - } - ]; - - bell = { - duration = 100; - color = "#000000"; - }; - - window.dynamic_title = true; - - scrolling.history = 100000; - - window.opacity = 0.95; - }; - settings = { - dark = commonSettings // { - colors = colorschemes.solarized-dark; - }; - light = commonSettings // { - colors = colorschemes.solarized-light; - }; - }; -in -{ - - programs.alacritty = { - enable = nixosConfig.jalr.gui.enable; - }; - - # The option `home-manager.users.jalr.xdg.configFile.dark.alacritty/alacritty-dark.yml' does not exist - - /* - xdg.configFile = builtins.mapAttrs (colorScheme: cfg: { - "alacritty/alacritty-${colorScheme}.yml" = lib.replaceStrings [ "\\\\" ] [ "\\" ] (builtins.toJSON cfg); - }) settings; - */ - - xdg.configFile = lib.attrsets.mapAttrs' - (colorScheme: cfg: lib.attrsets.nameValuePair "alacritty/alacritty-${colorScheme}.yml" { - text = lib.replaceStrings [ "\\\\" ] [ "\\" ] (builtins.toJSON cfg); - }) - settings; - - programs.fish.functions = { - ssh = { - description = "ssh wrapper function"; - wraps = "ssh"; - body = '' - if [ "$TERM" = alacritty ] - TERM=xterm-256color command ssh $argv - else - command ssh $argv - end - ''; - }; - }; -} diff --git a/home-manager/modules/cli.nix b/home-manager/modules/cli.nix deleted file mode 100644 index 2a11311..0000000 --- a/home-manager/modules/cli.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -{ - home.packages = with pkgs; [ - cached-nix-shell - file - htop - jq - lsof - ncdu - ripgrep - ] ++ (if ! nixosConfig.jalr.workstation.enable then [ ] else [ - direnv - dnsutils - screen - speedtest-cli - usbutils - wget - whois - yt-dlp - ]); -} diff --git a/home-manager/modules/communication/default.nix b/home-manager/modules/communication/default.nix deleted file mode 100644 index 6be079f..0000000 --- a/home-manager/modules/communication/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ nixosConfig, ... }: - -{ - imports = [ - ./ferdium.nix - ./mumble.nix - ./qtox.nix - ./telegram-desktop.nix - ]; -} diff --git a/home-manager/modules/communication/ferdium.nix b/home-manager/modules/communication/ferdium.nix deleted file mode 100644 index ee8f39e..0000000 --- a/home-manager/modules/communication/ferdium.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: - -lib.mkIf nixosConfig.jalr.tradebyte.enable { - home.packages = with pkgs; [ - master.ferdium - ]; -} diff --git a/home-manager/modules/default.nix b/home-manager/modules/default.nix deleted file mode 100644 index b5f38f0..0000000 --- a/home-manager/modules/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ nixosConfig, ... }: - -{ - imports = [ - ./${nixosConfig.jalr.terminalEmulator}.nix - ./aws.nix - ./claws-mail.nix - ./cli.nix - ./communication - ./direnv.nix - ./dynamic-colors.nix - ./firefox - ./fish.nix - ./fpv.nix - ./git.nix - ./gnuradio.nix - ./graphics - ./gui.nix - ./jameica.nix - ./kicad.nix - ./mpv.nix - ./mute-indicator.nix - ./neo.nix - ./neovim.nix - ./obs-studio - ./openscad.nix - ./pass.nix - ./pcmanfm.nix - ./python.nix - ./sound - ./sway - ./terraform.nix - ./tmux.nix - ./tor-browser.nix - ./vdirsyncer.nix - ]; - - programs.nix-index.enable = true; -} diff --git a/home-manager/modules/dynamic-colors.nix b/home-manager/modules/dynamic-colors.nix deleted file mode 100644 index d9d05b5..0000000 --- a/home-manager/modules/dynamic-colors.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: - - -let dynamic-colors = pkgs.writeShellScriptBin "dynamic-colors" /* bash */ '' - case "''$1" in - light|dark) - if [ -e "''$HOME/.config/alacritty/alacritty-''$1.yml" ]; then - ln -sf "''$HOME/.config/alacritty/alacritty-''$1.yml" "$HOME/.config/alacritty/alacritty.yml" - fi - ;; - *) - echo "unknown command ''$1" >&2 - exit 1 - esac -''; -in -{ - home.packages = [ - dynamic-colors - ]; -} diff --git a/home-manager/modules/firefox/default.nix b/home-manager/modules/firefox/default.nix deleted file mode 100644 index 39e227c..0000000 --- a/home-manager/modules/firefox/default.nix +++ /dev/null @@ -1,102 +0,0 @@ -{ nixosConfig, pkgs, ... }: -{ - programs.firefox = { - enable = nixosConfig.jalr.gui.enable; - package = pkgs.firefox-esr; - profiles = { - default = { - extensions = with pkgs.nur.repos.rycee.firefox-addons; [ - tree-style-tab - ublock-origin - umatrix - violentmonkey - ]; - settings = { - #"browser.startup.homepage" = "https://nixos.org"; - #"browser.search.region" = "GB"; - #"browser.search.isUS" = false; - #"distribution.searchplugins.defaultLocale" = "en-GB"; - #"general.useragent.locale" = "en-GB"; - #"browser.bookmarks.showMobileBookmarks" = true; - "app.normandy.enabled" = false; - "app.shield.optoutstudies.enabled" = false; - "app.update.auto" = false; - "browser.ctrlTab.sortByRecentlyUsed" = true; - "browser.fixup.alternate.enabled" = false; - "browser.formfill.enable" = false; - "browser.link.open_newwindow.restriction" = 0; - "browser.newtabpage.enabled" = false; - "browser.ping-centre.telemetry" = false; - "browser.safebrowsing.downloads.enabled" = false; - "browser.safebrowsing.downloads.remote.block_dangerous" = false; - "browser.safebrowsing.downloads.remote.block_dangerous_host" = false; - "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false; - "browser.safebrowsing.downloads.remote.block_uncommon" = false; - "browser.safebrowsing.downloads.remote.enabled" = false; - "browser.safebrowsing.downloads.remote.url" = ""; - "browser.safebrowsing.malware.enabled" = false; - "browser.safebrowsing.phishing.enabled" = false; - "browser.safebrowsing.provider.google.advisoryURL" = ""; - "browser.safebrowsing.provider.google.gethashURL" = ""; - "browser.safebrowsing.provider.google.lists" = ""; - "browser.safebrowsing.provider.google.reportMalwareMistakeURL" = ""; - "browser.safebrowsing.provider.google.reportPhishMistakeURL" = ""; - "browser.safebrowsing.provider.google.reportURL" = ""; - "browser.safebrowsing.provider.google.updateURL" = ""; - "browser.safebrowsing.provider.google4.advisoryURL" = ""; - "browser.safebrowsing.provider.google4.dataSharingURL" = ""; - "browser.safebrowsing.provider.google4.gethashURL" = ""; - "browser.safebrowsing.provider.google4.lists" = ""; - "browser.safebrowsing.provider.google4.reportMalwareMistakeURL" = ""; - "browser.safebrowsing.provider.google4.reportPhishMistakeURL" = ""; - "browser.safebrowsing.provider.google4.reportURL" = ""; - "browser.safebrowsing.provider.google4.updateURL" = ""; - "browser.safebrowsing.provider.mozilla.gethashURL" = ""; - "browser.safebrowsing.provider.mozilla.lists" = ""; - "browser.safebrowsing.provider.mozilla.updateURL" = ""; - "browser.search.suggest.enabled" = false; - "browser.search.widget.inNavBar" = true; - "browser.startup.page" = 0; - "extensions.pocket.enabled" = false; - "extensions.update.enabled" = false; - "identity.fxaccounts.enabled" = false; - "keyword.enabled" = false; - "network.captive-portal-service.enabled" = false; - "network.predictor.enabled" = false; - "privacy.donottrackheader.enabled" = true; - "startup.homepage_welcome_url" = about:blank; - "toolkit.legacyUserProfileCustomizations.stylesheets" = true; - "toolkit.telemetry.archive.enabled" = false; - "toolkit.telemetry.bhrPing.enabled" = false; - "toolkit.telemetry.firstShutdownPing.enabled" = false; - "toolkit.telemetry.newProfilePing.enabled" = false; - "toolkit.telemetry.server" = http://127.0.0.1:4711; - "toolkit.telemetry.server_owner" = ""; - "toolkit.telemetry.shutdownPingSender.enabled" = false; - "toolkit.telemetry.updatePing.enabled" = false; - "urlclassifier.downloadAllowTable" = ""; - "urlclassifier.downloadBlockTable" = ""; - "urlclassifier.malwareTable" = ""; - "urlclassifier.phishTable" = ""; - "datareporting.healthreport.uploadEnabled" = ""; - "app.normandy.api_url" = ""; - "breakpad.reportURL" = ""; - "browser.region.network.url" = ""; - "browser.search.geoSpecificDefaults.url" = ""; - "browser.shell.checkDefaultBrowser" = false; - - "privacy.userContext.enabled" = true; - "privacy.userContext.ui.enabled" = true; - "network.dnsCacheExpiration" = 0; - - # disable disk cache to reduce ssd writes - "browser.cache.disk.enable" = false; - "browser.cache.memory.enable" = true; - "browser.cache.memory.capacity" = -1; - }; - userChrome = builtins.readFile ./userChrome.css; - - }; - }; - }; -} diff --git a/home-manager/modules/fish.nix b/home-manager/modules/fish.nix deleted file mode 100644 index f14154f..0000000 --- a/home-manager/modules/fish.nix +++ /dev/null @@ -1,201 +0,0 @@ -{ config, pkgs, ... }: -{ - home.packages = with pkgs; [ - fzf - ]; - programs.fish = { - enable = true; - plugins = [ - { - name = "theme-agnoster"; - src = pkgs.fetchFromGitHub { - owner = "oh-my-fish"; - repo = "theme-agnoster"; - rev = "c142e802983bd1b34b4d91efac2126fc5913126d"; - sha256 = "0PLx626BWoBp/L6wgkB4o+53q8PymiEE/rTu2mfzHhg="; - fetchSubmodules = true; - }; - } - { - name = "fzf"; - src = pkgs.fetchFromGitHub { - owner = "jethrokuan"; - repo = "fzf"; - rev = "479fa67d7439b23095e01b64987ae79a91a4e283"; - sha256 = "0k6l21j192hrhy95092dm8029p52aakvzis7jiw48wnbckyidi6v"; - fetchSubmodules = true; - }; - } - ]; - shellAliases = { - ls = "ls --color=auto"; - crontab = "crontab -i"; - }; - - shellAbbrs = { - lessr = "less -R"; - jqc = "jq -C"; - }; - - #interactiveShellInit = '' - # echo "programs.fish.interactiveShellInit" - #''; - shellInit = '' - # key bindings - bind \cr '__fzf_reverse_isearch' - - # PATH - set -U fish_user_paths $HOME/.local/bin $HOME/.local/bin/pio - - # pass - #set -x PASSWORD_STORE_ENABLE_EXTENSIONS true - set -x AWS_VAULT_BACKEND pass - set -x AWS_VAULT_PASS_PREFIX aws - complete -c pw --no-files -a '(__fish_pass_print_entries)' - - # colors - set -x GCC_COLORS 'error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' - - abbr --add v vim - - #alias cal='ncal -b -M' - alias myip='dig +short myip.opendns.com @resolver1.opendns.com' - - function hm -d 'merge history and delete failed commands' - history --merge - - if test -z "$fish_private_mode" && test -e "$__fish_user_data_dir/successful_commands" && test -e "$__fish_user_data_dir/failed_commands" - while read line; - if ! grep -qFx $line "$__fish_user_data_dir/successful_commands" - set hist_command (echo $line | base64 -d) - echo "deleting command: $hist_command" - echo "." - history delete --exact --case-sensitive $hist_command - end - end < "$__fish_user_data_dir/failed_commands" - echo -n > "$__fish_user_data_dir/successful_commands" - echo -n > "$__fish_user_data_dir/failed_commands" - end - end - hm - - # fancy tools - if which exa > /dev/null 2>&1 - alias l=exa - alias ll='exa -l --time-style=long-iso --git' - alias la='exa -la --time-style=long-iso --git' - alias tree='exa --tree' - alias llt='exa -s modified -l' - else - alias l=ls - alias ll='ls -l' - alias la='ls -la' - alias llt='ls -trl' - end - - if which rg > /dev/null 2>&1 - alias g=rg - complete -c g -w rg - else if which ag > /dev/null 2>&1 - alias g=ag - complete -c g -w ag - else - alias g='grep --color=auto' - complete -c g -w grep - end - - function jqless -d 'jq -C [args] | less -R' - jq -C $argv | less -R - end - - # NixOS direnv - if which direnv > /dev/null - eval (direnv hook fish) - end - - function __cut_commandline -d 'cut commandline and paste it later' - set -g commandline_buffer (commandline) - commandline "" - end - - - - function __postexec --on-event fish_postexec - if test $status -ne 0 - if test -z "$hist_cmd" - if test -z "$fish_private_mode" - echo $argv[1] | base64 >> "$__fish_user_data_dir/failed_commands" - end - end - else - if test -z "$fish_private_mode" - echo $argv[1] | base64 >> "$__fish_user_data_dir/successful_commands" - end - commandline $commandline_buffer - set -e commandline_buffer - end - end - - function dirh-nocolor --description "Print the current directory history (the prev and next lists)" - set -l options h/help - argparse -n dirh --max-args=0 $options -- $argv - or return - - if set -q _flag_help - __fish_print_help dirh - return 0 - end - - set -l dirc (count $dirprev) - if test $dirc -gt 0 - set -l dirprev_rev $dirprev[-1..1] - # This can't be (seq $dirc -1 1) because of BSD. - set -l dirnum (seq 1 $dirc) - for i in $dirnum[-1..1] - printf '%s\n' $dirprev_rev[$i] - end - end - - echo $PWD - - set -l dirc (count $dirnext) - if test $dirc -gt 0 - set -l dirnext_rev $dirnext[-1..1] - for i in (seq $dirc) - printf '%s\n' $dirnext_rev[$i] - end - end - end - - function dirh-fzf -d 'directory history fuzzy finder' - builtin cd (dirh-nocolor | uniq | fzf) - end - - bind \ed 'dirh-fzf' - ''; - }; - - xdg.configFile."fish/completions/mycli.fish".text = '' - complete -e -c mycli - complete -c mycli -f -s h -l host -d "Host address of the database." - complete -c mycli -f -s P -l port -d "Port number to use for connection." - complete -c mycli -f -s u -l user -d "User name to connect to the database." - complete -c mycli -f -s S -l socket -d "The socket file to use for connection." - complete -c mycli -f -s p -l pass \ - -l password -d "Password to connect to the database." - complete -c mycli -f -s V -l version -d "Output mycli's version." - complete -c mycli -f -s v -l verbose -d "Verbose output." - complete -c mycli -f -s d -l dsn -d "Use DSN configured into the [alias_dsn] section of myclirc file." - complete -c mycli -f -l list-dsn -d "list of DSN configured into the [alias_dsn] section of myclirc file." - - complete -c mycli -f -s t -l table -d "Display batch output in table format." - complete -c mycli -f -l csv -d "Display batch output in CSV format." - complete -c mycli -f -l warn \ - -l no-warn -d "Warn before running a destructive query." - complete -c mycli -f -s e -l execute -d "Execute command and quit." - - - complete -c mycli -f -s h -l host -r -a '(__fish_print_hostnames)' - complete -c mycli -f -s d -l dsn -r -a '(mycli --list-dsn)' - ''; -} diff --git a/home-manager/modules/gui.nix b/home-manager/modules/gui.nix deleted file mode 100644 index a133844..0000000 --- a/home-manager/modules/gui.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.jalr.gui.enable { - home.packages = with pkgs; [ - evince - gcr # required for pinentry-gnome - geeqie - mpv - networkmanagerapplet - networkmanagerapplet - pinentry-gnome - streamlink - vlc - xdg_utils - ]; -} diff --git a/home-manager/modules/neovim.nix b/home-manager/modules/neovim.nix deleted file mode 100644 index 9986aa5..0000000 --- a/home-manager/modules/neovim.nix +++ /dev/null @@ -1,174 +0,0 @@ -{ lib, nixosConfig, config, pkgs, ... }: -{ - home.sessionVariables = { - EDITOR = "nvim"; - }; - programs.neovim = { - enable = true; - vimAlias = true; - extraConfig = '' - " use space as leader - let mapleader = " " - - colorscheme NeoSolarized - - """"""""""""""""" - " Swap and undo " - set noswapfile - set nobackup - if has('persistent_undo') - " yay persistent undo - :silent !mkdir -p ~/.local/vim-undo - set undofile - set undodir=~/.local/vim-undo - endif - - cabbr %% expand('%:p:h') - - set listchars=trail:·,precedes:«,extends:»,eol:↲,tab:▸\ - nmap c :set list! - - set smartcase - set hlsearch - nnoremap :nohlsearch:set nolist - - " highlight whitespace - highlight ExtraWhitespace ctermbg=red guibg=red - highlight WrongIndent ctermbg=2 guibg=blue - match ExtraWhitespace /\s\+$/ - augroup highlight_extra_whitespace - autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ - autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ gd lua vim.lsp.buf.definition() - nnoremap gi lua vim.lsp.buf.implementation() - nnoremap gr lua vim.lsp.buf.references() - nnoremap gD lua vim.lsp.buf.declaration() - nnoremap ge lua vim.lsp.diagnostic.set_loclist() - nnoremap K lua vim.lsp.buf.hover() - nnoremap f lua vim.lsp.buf.formatting() - nnoremap rn lua vim.lsp.buf.rename() - - nnoremap a lua vim.lsp.buf.code_action() - xmap a lua vim.lsp.buf.range_code_action() - - lua require('init') - ''; - - # nix-env -f '' -qaP -A vimPlugins - plugins = with pkgs.vimPlugins; [ - #Valloric/MatchTagAlways - #frankier/neovim-colors-solarized-truecolor-only - #nvie/vim-rst-tables - NeoSolarized - deoplete-nvim - editorconfig-vim - nvim-lspconfig - vim-gitgutter - vim-indent-guides - vim-nix - vim-puppet - vim-terraform - ]; - }; - - xdg.configFile."nvim/lua/init.lua".text = builtins.concatStringsSep "\n" ( - [ - '' - -- init.lua - -- this configuration applies to servers and workstations - '' - ] ++ lib.optional nixosConfig.jalr.workstation.enable ( - '' - -- this configuration applies to workstations only - -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md - local lsp = require('lspconfig') - '' + - builtins.concatStringsSep "\n" ( - lib.mapAttrsToList - ( - lang: cfg: "lsp.${lang}.setup\n" + lib.generators.toLua { asBindings = false; indent = " "; } cfg - ) - { - # C and C++ - ccls = { - cmd = [ "${pkgs.ccls}/bin/ccls" ]; - }; - - # Nix - rnix = { - cmd = [ "${pkgs.rnix-lsp}/bin/rnix-lsp" ]; - }; - - # Python - pylsp = { - cmd = [ "${pkgs.python310Packages.python-lsp-server}/bin/pylsp" ]; - settings = { - # https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md - pylsp = { - plugins = { - flake8 = { - enabled = true; - executable = "${pkgs.python310Packages.flake8}/bin/flake8"; - }; - jedi_completion = { enabled = true; }; - jedi_definition = { enabled = true; }; - jedi_hover = { enabled = true; }; - jedi_references = { enabled = true; }; - jedi_signature_help = { enabled = true; }; - jedi_symbols = { enabled = true; }; - mccabe = { enabled = true; }; - preload = { enabled = true; }; - pycodestyle = { enabled = true; }; - pyflakes = { enabled = true; }; - rope_completion = { enabled = true; }; - yapf = { enabled = true; }; - }; - }; - }; - }; - - # Ruby - solargraph = { - cmd = [ "${pkgs.solargraph}/bin/solargraph" "stdio" ]; - }; - - # Rust - rust_analyzer = { - cmd = [ "${pkgs.rust-analyzer}/bin/rust-analyzer" ]; - }; - - # Bash - bashls = { - cmd = [ "${pkgs.nodePackages.bash-language-server}/bin/bash-language-server" "start" ]; - }; - - # Terraform - terraform_lsp = { - cmd = [ "${pkgs.terraform-lsp}/bin/terraform-lsp" "serve" ]; - }; - - # YAML - yamlls = { - cmd = [ "${pkgs.nodePackages.yaml-language-server}/bin/yaml-language-server" "--stdio" ]; - settings = { - yaml = { - keyOrdering = false; - }; - }; - }; - } - ) - ) - ); -} diff --git a/home-manager/modules/pass.nix b/home-manager/modules/pass.nix deleted file mode 100644 index ce3e446..0000000 --- a/home-manager/modules/pass.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ nixosConfig, config, pkgs, ... }: - -let - pw = pkgs.writeScriptBin "pw" '' - p="$(${pkgs.pass}/bin/pass show "$1")" - - copy_line() { - echo -n "$p" | ${pkgs.gnused}/bin/sed -n "$1"p | ${pkgs.wl-clipboard}/bin/wl-copy -o -f - } - - echo "username" - copy_line 2 - echo "password" - copy_line 1 - ''; -in -{ - home.packages = [ - pw - ] ++ - ( - if nixosConfig.jalr.gui.enable - then with pkgs; [ - qtpass - pass-wayland - ] - else [ ] - ); -} diff --git a/home-manager/modules/solarized.nix b/home-manager/modules/solarized.nix deleted file mode 100644 index c814420..0000000 --- a/home-manager/modules/solarized.nix +++ /dev/null @@ -1,23 +0,0 @@ -builtins.mapAttrs - (name: hex: { - inherit hex; - rgb = builtins.concatStringsSep "," (map (f: toString (builtins.fromTOML "i = 0x${f hex}").i) (map (pos: builtins.substring pos 2) [ 1 3 5 ])); - }) -{ - base00 = "#657b83"; - base01 = "#586e75"; - base02 = "#073642"; - base03 = "#002b36"; - base0 = "#839496"; - base1 = "#93a1a1"; - base2 = "#eee8d5"; - base3 = "#fdf6e3"; - blue = "#268bd2"; - cyan = "#2aa198"; - green = "#859900"; - magenta = "#d33682"; - orange = "#cb4b16"; - red = "#dc322f"; - violet = "#6c71c4"; - yellow = "#b58900"; -} diff --git a/home-manager/modules/sway/waybar.nix b/home-manager/modules/sway/waybar.nix deleted file mode 100644 index 4ac79e0..0000000 --- a/home-manager/modules/sway/waybar.nix +++ /dev/null @@ -1,459 +0,0 @@ -{ config, lib, nixosConfig, pkgs, ... }: -let - watchUserUnitState = unit: started: stopped: pkgs.writeShellScript "watch-user-unit-${unit}-state" '' - ${pkgs.systemd}/bin/journalctl --user -u ${unit} -t systemd -o cat -f \ - | ${pkgs.gnugrep}/bin/grep --line-buffered -Eo '^(Started|Stopped)' \ - | ${pkgs.jq}/bin/jq --unbuffered -Rc 'if . == "Started" then ${builtins.toJSON started} else ${builtins.toJSON stopped} end' - ''; - - toggleUserUnitState = unit: pkgs.writeShellScript "toggle-user-unit-${unit}-state" '' - if ${pkgs.systemd}/bin/systemctl --user show ${unit} | ${pkgs.gnugrep}/bin/grep -q ActiveState=active; then - ${pkgs.systemd}/bin/systemctl --user stop ${unit} - else - ${pkgs.systemd}/bin/systemctl --user start ${unit} - fi - ''; - - # for fine-grained control over spacing - thinsp = " "; - - solarized = import ../solarized.nix; -in -{ - # home-manager’s waybar module performs additional checks that are overly strict - xdg.configFile."waybar/config".text = lib.generators.toJSON { } { - layer = "top"; - position = "top"; - height = 24; - - modules-center = [ ]; - modules-left = [ - "sway/workspaces" - "sway/mode" - ]; - modules-right = [ - "tray" - "custom/screencast" - "custom/redshift" - "idle_inhibitor" - "backlight" - "mpd" - "pulseaudio" - "network" - "custom/vpn" - "memory" - "cpu" - "temperature" - "battery" - "clock" - "custom/calendar" - ]; - - "sway/workspaces" = { - disable-scroll = true; - }; - "sway/mode" = { - format = "{}"; - }; - - tray = { - spacing = 5; - }; - "custom/redshift" = { - exec = watchUserUnitState - "gammastep" - { class = "active"; } - { class = "inactive"; }; - on-click = toggleUserUnitState "gammastep"; - return-type = "json"; - format = "󰌵"; - tooltip = false; - }; - idle_inhibitor = { - format = "{icon}"; - format-icons = { - activated = "󰈈 "; - deactivated = "󰈉 "; - }; - }; - "custom/screencast" = { - exec = pkgs.writeScript "screencast-monitor" /* python */ '' - #!${pkgs.python3}/bin/python3 - import subprocess - import sys - - active_outputs = 0 - - with subprocess.Popen( - ["${pkgs.coreutils}/bin/stdbuf", "-o0", "${nixosConfig.services.pipewire.package}/bin/pw-link", "-m", "-o", "xdg-desktop-portal-wlr"], - stdout=subprocess.PIPE, - text=True, - ) as proc: - for line in proc.stdout: - action = line.split(" ")[0] - if action == "=" or action == "+": - active_outputs += 1 - elif action == "-": - active_outputs -= 1 - else: - print(f"Invalid action {action} (in line {line})", file=sys.stderr) - - if active_outputs > 0: - print("󱒃") - else: - print() - - sys.stdout.flush() - ''; - format = "{}"; - tooltip = false; - }; - backlight = { - format = "{percent}% {icon}"; - format-icons = [ "󰛩" "󱩎" "󱩏" "󱩐" "󱩑" "󱩒" "󱩓" "󱩔" "󱩕" "󱩖" "󰛨" ]; - on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl -q set +5%"; - on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl -q set 5%-"; - }; - mpd = { - server = config.services.mpd.network.listenAddress; - format = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} – {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) 󰎈"; - format-disconnected = "Disconnected 󰎈"; - format-stopped = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped 󰎈"; - unknown-tag = "N/A"; - interval = 2; - tooltip-format = "MPD (connected)"; - tooltip-format-disconnected = "MPD (disconnected)"; - on-scroll-up = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume +2"; - on-scroll-down = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume -2"; - title-len = 48; - artist-len = 24; - consume-icons = { - on = "󰩫 "; - }; - random-icons = { - off = "󰒞 "; - on = "󰒝 "; - }; - repeat-icons = { - on = "󰑖 "; - }; - single-icons = { - on = "󰑘 "; - }; - state-icons = { - paused = "󰏤 "; - playing = "󰐊 "; - }; - }; - pulseaudio = { - format = "{volume}% {icon} {format_source}"; - format-bluetooth = "{volume}% {icon}󰗾{format_source}"; - format-bluetooth-muted = "{icon}󰗿{format_source}"; - format-muted = "󰝟 {format_source}"; - format-source = "{volume}% ${thinsp}"; - format-source-muted = "${thinsp}"; - format-icons = { - car = "󰄋 "; - default = [ "󰕿" "󰖀" "󰕾" ]; - hands-free = "󰋎"; - headphone = "󰋋"; - headset = "󰋎"; - phone = "󰏲"; - portable = "󰏲"; - }; - on-click-right = "${pkgs.pavucontrol}/bin/pavucontrol"; - }; - network = { - format-wifi = "{essid} ({signalStrength}%) 󰖩 "; - format-ethernet = "{ipaddr}/{cidr} 󰈀 "; - format-linked = "{ifname} (No IP) 󰈀 "; - format-disconnected = "Disconnected ⚠ "; - format-alt = "{ifname}: {ipaddr}/{cidr}"; - tooltip = false; - on-click-right = "${config.programs.alacritty.package}/bin/alacritty -e ${pkgs.networkmanager}/bin/nmtui"; - }; - "custom/vpn" = { - interval = 10; - exec = pkgs.writeShellScript "vpn-state" '' - ${pkgs.iproute}/bin/ip -j link \ - | ${pkgs.jq}/bin/jq --unbuffered --compact-output ' - [[.[].ifname | select(. | startswith("mullvad"))][] | split("-")[1] + " 󰌾${thinsp}"] as $conns - | { text: ($conns[0] // ""), class: (if $conns | length > 0 then "connected" else "disconnected" end) }' - ''; - return-type = "json"; - format = "{}"; - tooltip = false; - }; - memory = { - interval = 2; - format = "{:2}% 󰍛 "; - }; - cpu = { - interval = 2; - format = "{usage:2}%  "; - tooltip = false; - }; - temperature = { - critical-threshold = 80; - format = "{temperatureC}°C {icon}"; - format-icons = [ "" "" "" "" "" ]; - } // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { - hwmon-path = "/sys/class/hwmon/hwmon3/temp1_input"; - }); - battery = { - interval = 5; - format = "{capacity}% {icon}"; - format-charging = "{capacity}% "; - format-plugged = "{capacity}% x"; - format-alt = "{time} {icon}"; - format-icons = [ "󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; - states = { - critical = 15; - good = 95; - warning = 30; - }; - }; - clock = { - format = "{:%H:%M %Z}"; - format-alt = "{:%Y-%m-%d (%a)}"; - tooltip-format = "{:%Y %B}\n{calendar}"; - }; - "custom/calendar" = { - interval = 300; - exec = pkgs.writeScript "calendar" /* python */ '' - #!${pkgs.python3}/bin/python3 - import json - import subprocess - - - def khal(args): - completed = subprocess.run(["${pkgs.khal}/bin/khal"] + args, capture_output=True) - assert completed.returncode == 0 - return completed.stdout.decode("utf-8") - - - events_today = khal(["list", "today", "today", "-df", "", "-f", "{title}"]).rstrip().split("\n") - events_2d = khal(["list", "today", "tomorrow", "-df", "{name}, {date}"]).rstrip() - - if len(events_today) == 1 and events_today[0] == "No events": - events_today = [] - - if len(events_today) == 0: - text = "󰃮 " - else: - text = f"{len(events_today)} 󰃶 " - - print( - json.dumps( - { - "class": "active" if len(events_today) > 0 else "", - "text": text, - "tooltip": events_2d, - } - ) - ) - ''; - return-type = "json"; - format = "{}"; - }; - }; - xdg.configFile."waybar/style.css".text = '' - * { - border-radius: 0; - border: none; - font-family: "Iosevka Nerd Font"; - font-size: 14px; - min-height: 0; - transition-property: none; - } - - window#waybar { - background-color: ${solarized.base03.hex}; - color: ${solarized.base0.hex}; - } - - #workspaces button { - padding: 0 5px; - background-color: ${solarized.base03.hex}; - color: inherit; - border-bottom: 2px solid transparent; - } - - #workspaces button:hover { - background: ${solarized.base02.hex}; - box-shadow: inherit; - text-shadow: inherit; - } - - #workspaces button.focused { - border-bottom: 2px solid ${solarized.green.hex}; - } - - #workspaces button.urgent { - background-color: ${solarized.red.hex}; - } - - #mode { - background-color: ${solarized.base02.hex}; - font-style: italic; - } - - /* all modules on the right */ - #waybar > box > box:nth-child(3) > widget > label { - padding: 0 10px; - } - - #battery.charging { - color: ${solarized.base02.hex}; - background-color: ${solarized.green.hex}; - } - - @keyframes blink { - to { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - } - - #battery.critical:not(.charging), - #temperature.critical { - background-color: ${solarized.red.hex}; - animation-name: blink; - animation-duration: 0.5s; - /* FIXME use nearest neighbor interpolation if possible */ - animation-timing-function: cubic-bezier(1, 0, 0, 1); - animation-iteration-count: infinite; - animation-direction: alternate; - } - - #cpu { - background-color: ${solarized.cyan.hex}; - color: ${solarized.base02.hex} - } - - #memory { - background-color: ${solarized.yellow.hex}; - color: ${solarized.base02.hex} - } - - #backlight { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - - #network { - background-color: ${solarized.violet.hex}; - color: ${solarized.base02.hex} - } - - #custom-vpn { - background-color: ${solarized.blue.hex}; - color: ${solarized.base02.hex} - } - - #network.disconnected { - background-color: ${solarized.red.hex}; - } - - #pulseaudio { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - - #pulseaudio.muted { - background-color: ${solarized.base03.hex}; - color: ${solarized.base0.hex}; - } - - #temperature { - background-color: ${solarized.magenta.hex}; - color: ${solarized.base02.hex}; - } - - #idle_inhibitor.activated { - background-color: ${solarized.base3.hex}; - color: ${solarized.base03.hex}; - } - - #mpd { - background-color: ${solarized.green.hex}; - color: ${solarized.base02.hex}; - } - - #mpd.disconnected { - background-color: ${solarized.red.hex}; - } - - #mpd.stopped { - background-color: ${solarized.orange.hex}; - } - - #mpd.paused { - background-color: ${solarized.yellow.hex}; - } - - #custom-redshift { - color: ${solarized.base02.hex}; - } - - #custom-redshift.active { - background-color: ${solarized.red.hex}; - } - - #custom-redshift.inactive { - background-color: ${solarized.blue.hex}; - } - - #tray { - padding: 0 5px; - } - - #custom-notification_inhibitor.active { - background-color: ${solarized.base3.hex}; - color: ${solarized.base03.hex}; - } - - #custom-screencast { - background-color: ${solarized.red.hex}; - color: ${solarized.base03.hex}; - animation-name: blink; - animation-duration: 0.5s; - animation-timing-function: cubic-bezier(1, 0, 0, 1); - animation-iteration-count: infinite; - animation-direction: alternate; - } - - #custom-calendar.active { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - ''; - - systemd.user.services.waybar = { - Unit = { - Description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; - Documentation = "https://github.com/Alexays/Waybar/wiki/"; - PartOf = [ "sway-session.target" ]; - }; - - Install.WantedBy = [ "sway-session.target" ]; - - Service = { - # ensure sway is already started, otherwise workspaces will not work - ExecStartPre = "${config.wayland.windowManager.sway.package}/bin/swaymsg"; - ExecStart = "${pkgs.waybar}/bin/waybar"; - ExecReload = "${pkgs.utillinux}/bin/kill -SIGUSR2 $MAINPID"; - Restart = "on-failure"; - RestartSec = "1s"; - }; - }; - - # TODO: remove when https://github.com/nix-community/home-manager/issues/2064 - # is resolved - systemd.user.targets.tray = { - Unit = { - Description = "Home Manager System Tray"; - Requires = [ "graphical-session-pre.target" ]; - }; - }; -} diff --git a/home-manager/modules/sway/wofi.nix b/home-manager/modules/sway/wofi.nix deleted file mode 100644 index 2db0add..0000000 --- a/home-manager/modules/sway/wofi.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ nixosConfig, config, lib, pkgs, ... }: - -let - solarized = import ../solarized.nix; -in -{ - xdg.configFile."wofi/style.css".text = - let - # adding it to the header doesn’t work since the defaults overwrite it - commonConfig = /* ini */ '' - background=${lib.substring 1 6 solarized.base3} - border-bottom=${lib.substring 1 6 solarized.base2} - border=${lib.substring 1 6 solarized.base2} - button-background=${lib.substring 1 6 solarized.base3} - button-text=${lib.substring 1 6 solarized.base00} - ''; - in - /* css */ '' - window { - margin: 0px; - border: 3px solid ${solarized.base02.hex}; - border-radius: 8px; - background-color: rgba(${solarized.base03.rgb},0.8); - } - - #input { - margin: 5px; - border: none; - color: ${solarized.base0.hex}; - background-color: rgba(${solarized.base02.rgb},0.8); - } - - #inner-box { - margin: 5px; - border: none; - background: none; - } - - #outer-box { - margin: 5px; - border: none; - background: none; - } - - #scroll { - margin: 0px; - border: none; - } - - #text { - margin: 5px; - border: none; - color: ${solarized.base0.hex}; - } - - #entry:selected { - background-color: rgba(${solarized.base02.rgb},0.8); - } - - #entry:selected #text{ - color: ${solarized.green.hex}; - } - ''; -} diff --git a/home-manager/modules/terraform.nix b/home-manager/modules/terraform.nix deleted file mode 100644 index 32bcb73..0000000 --- a/home-manager/modules/terraform.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ config, pkgs, ... }: - -{ - home.packages = with pkgs; [ - terraform - ]; - - home.sessionVariables = { - TF_PLUGIN_CACHE_DIR = "$HOME/.local/share/terraform/plugins"; - }; -} diff --git a/home-manager/modules/tor-browser.nix b/home-manager/modules/tor-browser.nix deleted file mode 100644 index 6053e70..0000000 --- a/home-manager/modules/tor-browser.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.jalr.gui.enable { - home.packages = with pkgs; [ - ( - tor-browser-bundle-bin.override { - useHardenedMalloc = false; - } - ) - ]; -} diff --git a/home-manager/users/default.nix b/home-manager/users/default.nix deleted file mode 100644 index 26dd6aa..0000000 --- a/home-manager/users/default.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ lib, ... }: - -{ - options.jalr = { - git = { - user = { - name = lib.mkOption { - type = lib.types.str; - description = "name to use for git commits"; - }; - email = lib.mkOption { - type = lib.types.str; - description = "email to use for git commits"; - }; - }; - signByDefault = lib.mkEnableOption "GPG sign commits per default"; - }; - gpg.defaultKey = lib.mkOption { - type = lib.types.str; - description = "default gpg key id"; - }; - terminalEmulator = lib.mkOption { - type = lib.types.str; - description = "default Terminal emulator name"; - default = "alacritty"; - }; - }; -} diff --git a/home-manager/users/jal.nix b/home-manager/users/jal.nix deleted file mode 100644 index 805c890..0000000 --- a/home-manager/users/jal.nix +++ /dev/null @@ -1,222 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - userName = "jal"; - vpn_routes = [ - "10.18.0.0/16" # OEE VPC - "10.64.64.0/20" # CPS - "10.158.128.0/23" # approval - "10.158.224.0/20" # core production - "10.158.240.0/20" # core development - #"10.96.0.0/24" # CCS infrastructure - #"10.96.8.0/24" # Boomi - #"10.96.10.0/24" # Boomi (new) - "10.96.0.0/16" - "10.170.254.30/32" # core DNS resolver - ]; - vpnc-script = pkgs.writeShellScript "vpnc-script-tb" '' - cisco_split_inc="$CISCO_SPLIT_INC" - export CISCO_SPLIT_INC=0 - unset INTERNAL_IP4_DNS - - route_in_whitelist() { - for route in ${builtins.toString vpn_routes}; do - [ "$1" = "$route" ] && return 0 - done - return 1 - } - - routes() { - for i in $(seq 0 $((cisco_split_inc-1))); do - addr_var="CISCO_SPLIT_INC_''${i}_ADDR" - mask_var="CISCO_SPLIT_INC_''${i}_MASK" - masklen_var="CISCO_SPLIT_INC_''${i}_MASKLEN" - addr="''${!addr_var}" - mask="''${!mask_var}" - masklen="''${!masklen_var}" - if route_in_whitelist "$addr/$masklen"; then - case "$1" in - add) - if [ -n "$NETGW" ]; then - ip route add "$addr/$masklen" metric 100 dev "$TUNDEV" via "$NETGW" - else - ip route add "$addr/$masklen" metric 100 dev "$TUNDEV" - fi - ;; - remove) - ip route del "$addr/$masklen" dev "$TUNDEV" - ;; - esac - echo "allowing route '$addr/$masklen'" - else - echo "ignoring route '$addr/$masklen'" - fi - done - } - - case "$reason" in - pre-init|reconnect|attempt-reconnect) - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - ;; - connect) - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - routes add - ;; - disconnect) - routes remove - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - ;; - *) - echo "reason '$reason' is not implemented" >&2 - exit 1 - ;; - esac - ''; - tradebyte-vpn = pkgs.writeShellScriptBin "tradebyte-vpn" '' - [ $UID -ne 0 ] && exec sudo -- "$0" "$@" - /run/wrappers/bin/sudo -u "$SUDO_USER" ${pkgs.pass}/bin/pass show zalando | openconnect \ - --protocol=pulse \ - -u jlechner \ - --passwd-on-stdin \ - -i pulse \ - --pfs \ - --disable-ipv6 \ - --script=${vpnc-script} \ - https://remote.tradebyte.org | grep -v '^> ' - ''; - aws_defaults = { - sso = { - start_url = "https://d-9967250383.awsapps.com/start"; - region = "eu-central-1"; - role_name = "AdministratorAccess"; - }; - region = "eu-central-1"; - }; -in -{ - imports = [ - ./default.nix - ]; - - jalr = { - git = { - user = { - name = "Jakob Lechner"; - email = "jal@tradebyte.biz"; - }; - signByDefault = false; - }; - gpg.defaultKey = "FE170812543DF81393EA56BA5042B8317A10617E"; - aws = { - enable = true; - accounts = { - ops_testing = { - sso_account_id = 134848648016; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - core-production = { - sso_account_id = 455520445575; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - tbmeta-production = { - sso_account_id = 696695470425; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - abnahme = { - sso_account_id = 837645089494; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - core-develop = { - sso_account_id = 934000686307; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - infrastructure = { - sso_account_id = 994756397773; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - tbmeta-development = { - sso_account_id = 730951147261; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - }; - }; - }; - - users.users.${userName} = { - isNormalUser = true; - extraGroups = [ - "dialout" - "podman" - "libvirtd" - "lp" - "networkmanager" - "scanner" - "video" - "wheel" - "wireshark" - ]; # Enable ‘sudo’ for the user. - shell = pkgs.fish; - }; - - home-manager = { - useUserPackages = true; - useGlobalPkgs = true; - users.${userName} = { lib, pkgs, ... }: { - imports = [ ../modules ]; - config = { - home.stateVersion = config.system.stateVersion; - - home.packages = with pkgs; [ - mycli - timetrap - tradebyte-vpn - - # common - asciinema - bat - docker-compose - envsubst - exa - gnupg - nmap - psutils - pwgen - tig - vlc - xdg_utils - ]; - }; - }; - }; - - security.sudo.extraRules = [{ - users = [ userName ]; - commands = [ - { - command = "${tradebyte-vpn}/bin/tradebyte-vpn"; - options = [ "NOPASSWD" ]; - } - ]; - }]; -} diff --git a/home-manager/users/jalr.nix b/home-manager/users/jalr.nix deleted file mode 100644 index 3e3cbe6..0000000 --- a/home-manager/users/jalr.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ config, pkgs, ... }: - -{ - imports = [ - ./default.nix - ]; - - jalr = { - git = { - user = { - name = "Jakob Lechner"; - email = "mail@jalr.de"; - }; - signByDefault = true; - }; - gpg.defaultKey = "66FB54F6081375106EEBF651A222365EB448F934"; - }; - - users.users.jalr = { - isNormalUser = true; - extraGroups = [ - "audio" - "dialout" - "docker" - "libvirtd" - "lp" - "networkmanager" - "scanner" - "video" - "wheel" - "wireshark" - ]; # Enable ‘sudo’ for the user. - shell = pkgs.fish; - }; - - home-manager = { - useUserPackages = true; - useGlobalPkgs = true; - users.jalr = { lib, pkgs, ... }: { - imports = [ ../modules ]; - config = { - home.stateVersion = if config.system.stateVersion == "22.11" then "22.05" else config.system.stateVersion; - - home.packages = with pkgs; [ - cutecom - ghostscript - newsboat - pdftk - platformio - ptouch-print - qrencode - sshfs - tmate - - # common - asciinema - bat - docker-compose - envsubst - exa - gnupg - nmap - psutils - pwgen - tig - ]; - }; - }; - }; -} diff --git a/hosts/aluminium/configuration.nix b/hosts/aluminium/configuration.nix index 0228b51..3dfd6a3 100644 --- a/hosts/aluminium/configuration.nix +++ b/hosts/aluminium/configuration.nix @@ -1,21 +1,18 @@ -{ config, lib, pkgs, ... }: +{ config, ... }: -let - iptablesAppendIfMissing = rule: "iptables -C " + rule + " || iptables -A " + rule; - iptablesInsertIfMissing = rule: "iptables -C " + rule + " || iptables -I " + rule; -in { imports = [ ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ../../users/jalr ./services + ./ports.nix ]; - networking.hostName = "aluminium"; services.openssh.enable = true; security.sudo.wheelNeedsPassword = false; networking = { + hostName = "aluminium"; useDHCP = false; vlans = { lechner = { @@ -26,6 +23,10 @@ in id = 2; interface = "enp1s0"; }; + iot = { + id = 3; + interface = "enp1s0"; + }; pv = { id = 10; interface = "enp1s0"; @@ -34,6 +35,10 @@ in id = 11; interface = "enp1s0"; }; + sprechanlage = { + id = 12; + interface = "enp1s0"; + }; }; interfaces = { lechner.ipv4.addresses = [{ @@ -44,13 +49,21 @@ in address = "192.168.1.1"; prefixLength = 24; }]; + iot.ipv4.addresses = [{ + address = "192.168.2.1"; + prefixLength = 24; + }]; pv.ipv4.addresses = [{ address = "192.168.10.1"; prefixLength = 30; }]; heizung.ipv4.addresses = [{ address = "192.168.10.5"; - prefixLength = 24; + prefixLength = 30; + }]; + sprechanlage.ipv4.addresses = [{ + address = "192.168.10.9"; + prefixLength = 30; }]; enp2s0.useDHCP = false; }; @@ -62,19 +75,22 @@ in "voice" ]; }; - firewall = { - extraCommands = lib.concatStringsSep "\n" [ - (iptablesAppendIfMissing "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu") - (iptablesInsertIfMissing "INPUT -i voice -p udp -m udp --dport 5060 -j ACCEPT") - (iptablesInsertIfMissing "INPUT -s 217.10.68.150 -p udp --dport 5060 -j ACCEPT") - ]; + firewall.extraInputRules = '' + iifname "voice" udp dport 5059 accept + ip saddr 217.10.68.150 udp dport 5060 accept + ''; + nftables.tables.pppoe = { + family = "ip"; + content = '' + chain clamp { + type filter hook forward priority mangle; + oifname "ppp0" tcp flags syn tcp option maxseg size set rt mtu comment "clamp MSS to Path MTU" + } + ''; }; }; - sops.secrets.pap-secrets = { - sopsFile = ./secrets.yaml; - }; environment.etc."ppp/pap-secrets".source = config.sops.secrets.pap-secrets.path; services.pppd = { enable = true; @@ -120,7 +136,7 @@ in # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + system.stateVersion = "23.11"; # Did you read the comment? } diff --git a/hosts/aluminium/hardware-configuration.nix b/hosts/aluminium/hardware-configuration.nix index dcdb4c2..6b60bb6 100644 --- a/hosts/aluminium/hardware-configuration.nix +++ b/hosts/aluminium/hardware-configuration.nix @@ -1,7 +1,7 @@ # 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, modulesPath, ... }: { imports = @@ -59,33 +59,32 @@ bootloader = "grub2"; }; - fileSystems."/" = - { + fileSystems = { + "/" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; options = [ "subvol=root" ]; }; - - fileSystems."/home" = - { + "/proc" = { + device = "/proc"; + options = [ "nosuid" "noexec" "nodev" "hidepid=2" ]; + }; + "/home" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; - options = [ "subvol=home" ]; + options = [ "subvol=home" "nodev" "nosuid" ]; }; - - fileSystems."/nix" = - { + "/nix" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; - options = [ "subvol=nix" ]; + options = [ "subvol=nix" "nodev" ]; }; - - fileSystems."/boot" = - { + "/boot" = { device = "/dev/disk/by-uuid/695df89b-948d-4659-8f57-335e8b25a8c5"; fsType = "ext2"; + options = [ "nodev" "nosuid" "noexec" ]; }; - + }; swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking diff --git a/hosts/aluminium/ports.nix b/hosts/aluminium/ports.nix new file mode 100644 index 0000000..f83f360 --- /dev/null +++ b/hosts/aluminium/ports.nix @@ -0,0 +1,16 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + asterisk-rtp.udp = { from = 10000; to = 10200; }; + doorbell-audiosocket.tcp = 9092; + doorbell-webrtc-ice.tcp = 8189; + doorbell-webrtc.tcp = 8889; + esphome.tcp = 6052; + home-assistant.tcp = 8123; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + unifi-inform.tcp = 8080; + unifi-ui.tcp = 8443; + }; +} diff --git a/hosts/aluminium/secrets.yaml b/hosts/aluminium/secrets.yaml index f94720d..2ecd5a3 100644 --- a/hosts/aluminium/secrets.yaml +++ b/hosts/aluminium/secrets.yaml @@ -1,8 +1,10 @@ duckdns-secret: ENC[AES256_GCM,data:hp4aWnmTYKZhBehY0nuRV+H9bpCdK2uNqY3J0s1w6JsiyXip,iv:X0MtN+lqDqucgHOgS1D/RrMksNydLFW1/wqD47DWhqQ=,tag:+7qsJEJYzI+UUrdC6NZr4Q==,type:str] pap-secrets: ENC[AES256_GCM,data:UyC63/4EXZjypFlH7MLtJXpIBgD9P/Eolg2M1A==,iv:tf8W8rpRa487PIB9NW4NyDKgCoWYV/wDgs9MmKLZ/mc=,tag:r+zgW8XI9TUyoz56irYEdQ==,type:str] +myintercom-doorbell-password: ENC[AES256_GCM,data:waUUvHQ9BZFePQ==,iv:ev21SNOwzdNMc3Opo6Kgdd7daLNUf9e1/C9RtxQKV8U=,tag:aOi2f3VuR49J0sHNrVXW/A==,type:str] asterisk-pjsip: ENC[AES256_GCM,data:PMgHCdo7K1a9/OitWdUonJ66gr70uwYgylCCWAO9cYOeXdPTIFuFLHlgBIUUxfln3UqhquTzoTluZJW9vaSuzZGe1kLIYrb1hRyrM0HLCCQc8m46jN898le/9ZrEivxonWkxf4FTfpENIf7iEr5KHh4vfd4tr4IbORTFdpcbsy8pd5eyvS8G2z9dynIWS19zqrzfGrW6yZICzAJz28IQCiiHgpN16bqSwlcPm1UdX+qi0+ZJ3TAr16Px1F9VFOXtEsu4EZvJSomecJDuhjo3QzBFffXDL971of8KX05BJgtpP6SzIZXKfSWaOxaguctdFr2tScvze0o3FXpDoOn0cvinOdYQt1P2TzjFnBZ4I3N1turpD4be9xJ92coV/j1hBsZHj2mWE/iCdsrzj2uP/74b4Mo1BJZ6l1gXFg3OgyDXaVoMxAOnutelCEG0lf78hsJXF56aQ1LVSUly6ugZP4rMiPFg5oa7WfrIsVVURUt7WRFrDLCYIQVynpfeUxHshPSB+/jVvYLqie5XeNt8B8mgTJYFo5hFB28sa1beqYEA27QMT3gRvWivqDnuf8soVi/r3WREfnSCBujhzXQF/uJZEwqVEn0OQo9ICfJ8hqtvDiAw6Hb4Wn+0maoYQeKjbPHeL3kr1SUE/kU913FNig4Yn66QKYevLLIkd3uQ0GqTLcgn4Ttwu3qArlXXxrI4US8yA7XGQUutVadN7ayyZBbYnw+vUTlPfhSO+ridK3huGKnQfcPAbD31L11EeQBe2820Nba9Bb4d5QAkiGsNj5y9tZ4Vl6l2JErO63fVPKQ9fPxD3yYyZpP8Hm1e7Wl1eRsNtoWqkTRtno7hIpAYFoMYTUk2x5U/qZOgtRX0JHufi6+GXvPPlBaQNfiGzNlJjdmtTT6MGLPRQjsASGi00pSjKd4psAj9Uf8rttsHhJHvIRDRsiNSjae+JGbVlyyauU1JL44Qf+U+MaJDjkLagNqUZ9xgNFmXzr7st6bRFYCJHkmQC8bgJsdpwRMz3HjNzrKZRvRhHIiwT3d+oyrd9hoSQl3JkxcrD7AfEThrBQL9BpGCDcfr5RzfNv8Fb08tR7rlIzyb6Rw3eKlY1obfZRRNTF+iYlBDz8LLI+BwWqJiefbHB2F9nOC0of5Eqm5gjn+MXSKuSIP5ltDsjfO+m6q7c+t7udKwnJVnePtOnuf4uQpKfxjpld4e8Y1N9hyuKSjqEy83UB4yXJb1OoUAOXENvdPhGFDghmSC+ZVcCZRBG2k6d6MdXY6AkdjUAteDQLsDNMwpW8a8RwOXlDoAtxu7yEYP51BrHu2spagNfXMWHThnkcuR/TvqAPmcPlzVjcX+tnuU0k+JK5e4eWc+diTcvo8fpeaKi7A4uyGWRaZsoaauxsK1dEwIgmAAYyWc0Hl+Z49/dLW8kgr/Qh9N5SRRk/SLk4GvS0uyYYClN7G/7LdMDUwWifr32oqXEINDh0NEyehEJ9dEQsIIH5gR3OdlEAuL1C7/Js3/ZCdBREXRYt4y5y4TAO/kMmGgv7Y/Z2XVD0klXVBMvVnil4LJ0H5KF+RZC4j/C6acRBdrPaI0nlE3bfAbmizQN9D7jOj5BkkRzBaYlMaBuFKRKUA6CUanhUWhIn3ZlF3Z+o4PGB2c7EFXZN+PzOSgkQYUD7KtVW/QV94mxkcqN9mKe6mAbj87neN1IHhEkNOj7KJQP60pqDjx6N+WYFpD3sYvDcJDg2WFumR8F2v+jHx09v5AB1r6AzhPJ3TCwnHN4e1+Nexxlb91iPcoSmLRF3Fimn7307260CtaA70hngWHSRaBcKTXi3WL1v9kKOou2kKs1GMy5bjREtqheBxZ1i4x56VtANF9lo9UT+97qxuAqk08Rc4z9j5M8cJK/d1syRT0z/uAuTWlRgxdE/Fj/OlDNr/SnZw9CLkQ0SVJAuJFFg9EY0ru3PC9PDNt9CJiVy0GoeK0mv7ZkTv2o456kdzMpJPBwpKLIO9tpZBbNZrMn1HpLJrfXIvmuVDFmm3EH6FVhGoI+4yB11Eo/2aEMzUOEtn55KNeESkoVel6GgYiwrg1ZlQS7XhdCTGyCOMbFTOLHgUe4vaUfPBNOyLaLWE3ZiyGCxVb+nBltcPSDHrNtbc2fuPqVom3Z1wfmako1BGcwRzbLdaUPwuu6eRa/KxppPh/PoYTttPxOArql25BWAVTI6BIhlvGgZgqDRwihHBGt1uyXjwv4ufES5zgxhMB8mNqVnCSkcLXXyvpmCiB5kEv5+V4nCJIXSNbmym+V9tEzGh+cx8up24IHrg6gG28fHfMcV7Z+JzN86jogr+sgH9wigrcYcDqTE9lHJhaZlmNraTl8viAwEXkPC/dnQuPSTX5V1qeRtKo1oFkf9xnPhdVLq51GoVU+MhQqZsbnqymgKnPWTQq3Kyiux5go/Li0BqfiV+Wwpn+f3WXJ21aMpU2FfIR26z2DULlJUYDKoewmklq8vzk5iZ/tywPFGR1G0z8IM5jwr+qz0uEccAtulCWsQjtvw0kGLnTsoB2WNL4x0Kti/cE14purKaE65wMrBoG/mxd6R7ZHE7u/Uo1MDAsgqsS8MomCqyxC/1yH9BdhpXc6VZJpborqWQjW/kK8/OBxWFjfQgwvDGeQkgv2ShV0c8U6DgnS545Im9aAxQGvu1sXMhnVNQZdZ2Ta3Gz7bTHqkxB4/X7KGHdGSmw5s/RQfo0BkBBBLLTc49pcmJTxG5LPkRebCM8ANX57qj3u/D9wYumFKclTglNdrjaxSdh3zTb1kEQ0rn/D4z7lVNUsw7srUUZeEadg3xTZSmSustbziXvp51juiJeyPjVY2AlmbVVxU0O245kbyWA8lHcEluo+dfk0Rr9hDNHz35NxQRCslPHiSKswxfuPcqyzlSiBMLsMWrJ5/RyQJgaO/XJ/x3R2o4h+MiHtUKj91epxAIpYD8JqQ4eaUkP6GJRNDSNLK3VNP69Qecc7b6AvV5udzt2up0lp7OuzEZeT88Vg8YcZvOv1UTxmkI6dem1xi4imJs+V4OZrcSt9ZTlc34rc6/lvVxVQZs/1vADB0ZVk3jp24KWuRWFGacJqUIxW8TbI8N1DtmZcf7sqoQU1QPRzkOa/UYmzWablAP4B5M5WOjyr3YSJGOzHxN+GSSs4K4jHUon+LbpKxHL5KJUSsD+kZFTfsDauFhAzpFDhR2wW/XYLr0iTvKQ6+26dIpW65P8Egv+n/CXQE0wuJ1R5z0M4FucpUo+FTUIcww8cfqfHqMlMeKEFeu8/QNdZ0uj06Q8/j6E/OUjpxTIVRQBs4qaLWxMZv3zulCUe9Czr6c28NhewIJlLUxOnCVDo5pT1OmzZPghurNyhTBFP8PfJrRXN1h2uvXfGP46dgt9jgeqqQqP9xlq+fzo9cyEZ/n4nQvY+CBuOW9Cqo41zNB0PQ3tC9SU477gQkDrg0M6/bAk+xsqVg1DpZOSuRUQnOfbTdZ1CXhESy+dcri9BeKKcTCZ6aenvW4W4J6OV8en3L4jPFsgqEWJUk1qr9ggM5NXc7RIrR0eCsiR9V1gi4HWMF1roTZ3wK9NvdATj3HWTGssfdpXht/vjedIp+InNWBWjnBfIf7XWuPgiB/ZW9uew8g8vDLULGVtww==,iv:bFKc8e+3rLAHje8UWwY2elof5xqceTTWX1f7nkE91nM=,tag:NWMiljj8urTDoka5bkF0jg==,type:str] asterisk-ari: ENC[AES256_GCM,data:HnY7d3BdScb0bmsBVlsTHAMv2k8tyyA/,iv:q+NsCHcGGOCe6gdAHbFfjKvO4dyWoW/xI5jtngJmdds=,tag:e8kuEsEokf5lAAgO/coxTQ==,type:str] -asterisk-voicemail: ENC[AES256_GCM,data:UbMjSgxZpUDzPpeVPQKfDEwKe/KgZJdMf+1q7YyPtjqiw80flSo3NqLREtuGshHVFqTSS+UFoucpJMx0qB3h4YCqJgSsJQB692q1fra1aLtnw08u1eEkqyi4EKBN4KuaD8E6zggN7Zor7IqEPrAp3H/JAWvoMqGaWZTn4vJbNnzFHp9JPvzyf0emmXTXm+SJFjoSjuT3tgrimQ9UsgUrX4Nxx6ZTMKnRU72+sEaRqtSjlYfk4aYveB82oV+s1iexGidy8vgVEJzRxkkbc87+2BrSTGW63VjIMLSYAFYOQp+hsP263lRJ8ZJYdJqrk49KXMpz8M5/lgk4P78Z/1PQ741CxpzLfo2oXij6jNPaH4jGujrsqK6lUSWi4g9rft76hYZF15absiLbvJZLIXcwTG4OsFkd+c7Ths7j/P/ttjBcnOgykz8ywW8XsSISBXehaO6FwPk37rddUHeEYOvwzKbH1qpgbtS5z/BKONKxqh+8FY5Wf3n80/0SbBQ3JlBnuTuqHQQlKCv2zkzGQv3aeu1WgqmM1+OH3BgSFM/F4o/gINWsOdU0DD9rNobb909mJspY3E1iEdPEElrMeZ/xKc2Zl1sKQtZIyZMRm0gYo/H/hODmBS3bqvTapmeMWpe5SCyw03FTp1O//brPHLdNqglkll9qqgsf4JUnCqefK5qyqqS4kPcDAzSBrYcBZLsKg5DnZbedD/rFNPDrlTaADMo4HkpwtE3U8jznVSDqAL3PfWvGk/bYLxaObXIzPpZybL/ILnnV5iifqSkhqhVwUaCX6nH0uB0mpiwXFbrFRYY6egqKrxtLoMEh5qT3wFdtKTt9a0Nnytvh/00vpv9yiHVEgkXGFkSC4Y3n3rSImWONKO+mYvlPH5kKM9vT9nIKBRBC7rmM3NRCKYCtPyOZlmTb6spADscUC6kux51Abd7GT/XWCmETB6cMNN2IQUVuJvcXX8IJay/mYYdgwY1DtVAWSkJlnJGAuvNQL4/v5v08u8GrnCO89aZwbA56zEulOBVr5iINm0BmqCAg9OHJGjlq4eK0/5bT,iv:ra1EmIfT7Pwz5GVidrlTDN/Ox4ErsAvQCe7hoQoTXRg=,tag:T7nCiJ+8djxFbTJfB9Hhxw==,type:str] +asterisk-voicemail: ENC[AES256_GCM,data:uyXeBP+9WkfVot4Ot3vwv3OEZfoVDK2I+lvaPpGJTZp16YNtP+uxNiW2ynewQlORCTY59bP1jW3bQdT/ASGsErOrhInYSytTyfdZ51BF9+jz0TH6oWxsSuuawTrkC8jvJOpejt6XuGoYbbqlM/VL1xzgDkq3ztTxaHTfdTonQij2Q4cYddMRHWIEuBCK7FU2TlHAJeIFZvtE0MiyNNT3rEWSs1xcljTGfMjkoMd+FI1uZSQT4r0kAaPPkvCWcAGH6R+F0Ue++i9TuLhu+sDV+X6u3N/garDW74H0bOcLJysImtuPXh1aXuBkHQuC1Liss/IF4NDjtDDhpfc0eePR5MWv/Kj0q+VFJiUPY6XnWh6fG9I2yY22+I7eAAg/xWVZBXPWbFHRz8jm1owp4ln6/hcrJOw6Fzw8tZ6Jd9nciOeOmR1KtjEzklPP5kP1YQPtGio/LnOaAAhTHy16MbWf/Ey4S30+eHB+joD8OM93+YxxrdKNE6XXEcAhkdpHYecrvz4Co1fhY7ZoOnNvA8Juup/7PMyNEU/Fy4Pta34aT/j1s7de2vTpRNBeecWvgFA9Qd7Re/2XPqOAkpduxDniwsUdb52oL39MBoOCY8brmXn2J/mMDeOmoqvjRHsPZsajPTAqF/nqRB8VpwoZAKAx59DYBGgmHz7/7JRX9NXOAus1yLbMfVqDftk6+KTFQ9wCqei3jaI/K5AJrSEwlZG0BLoDefIGXT5f8bNNgSn865j2RP+FLa6W3/u5t+k=,iv:/phktIxMdDO5Nrum7hf3oLDmQO04lrkvFuHNw77aRks=,tag:7OUg0BG9X7nBHWiQNaSOEQ==,type:str] +esphome: ENC[AES256_GCM,data:2pFVokO8YTyKa1F7EePo6wIS3y6prL8SSkxypWZkHl3Ye6Qg0eqZ4du/iwLIXQpJoc6R3uU7D6eIQEVOGbwqYp6+F0CW17F89k9c/VLHQHRpWbA20GgLr7X4fZ8xdbp7HCLpVxRsdzDz8aoARfV8Cn6T7Uo80ah1rMDnTj10WI+Yu6xVqVwPNWrSk9NUGKMK32M2slk=,iv:Xla0c4d9rxn06upy7GTbWBQ8pzl+gLnIw+Rf6hqQlhk=,tag:S+clc2ctuOA6lsInSFm93Q==,type:str] sops: kms: [] gcp_kms: [] @@ -12,25 +14,25 @@ sops: - recipient: age1ne08hny30vrkejqhh7dcx4ql6dmkx6jw9dqkf3cz7mzvt53njy0qh59w44 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBodUx5V25ETmJLTi9EZlRU - ZnYweXliTDl4ZUlvcmliTjhQRkpzU0pkNXlZCjFtYU5ySWFxOGlNL29SR2RJZHNu - UHJ4YWE4UWJVeEJBUXJwaHJBd292REkKLS0tIDV2WlppeUxIOWFPTHlRYTBaMzA0 - MU41eU8zeTRRUlZyUXV0U1N6U0NRNnMKZK3vfyRRr7Iu6HfpdpmDTKzUbEnCnW9l - rGjFmY9VX2q9w3j/4E5uUToQfeGMqqBTOFUB3hNgU8K5ZT7wMbOXAg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEZmNOcVlKNmZCdWN5NVBy + U3VRbXl3OUljWCtITXZCQTdrVkIxOEtDZHg4CktBNjVKRmVucjRpYXo3WXFWd1VV + MFpGdWIvTmNHRlJ4akxUQkZzWUtXVUkKLS0tIGs3NlNXREVkT1Nta2prSXk4QkV0 + NEtzRXY1Q1Njelc1YXNWVE9Jd2NnOFEKjOWHaxO5fF5l+c1Hv6QLBQajrvu1VimZ + Hqk0GYrFpfpFtbhBRyrYgmNuX/qIRMHemdXcNKDYcj0WXgsdVqH7Qw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-12-06T22:00:11Z" - mac: ENC[AES256_GCM,data:rg0vVhW4rXOJb72LDCLNTQtCHQu165knPZuGT56zoGGWHPDDTWnqt/WaBHyUoIBXWe286jW+aV0hFZMRCrS8cu+XSTztmXDIuNaYU7s8XW8ZzISUTCHDVUiYRSjaZXvCprr0cpiQoJ9dAl+Rm5m50elCdC8E7VaNGEYTIYWYLtk=,iv:7R8xt3WJKdcMrUdL/byMIbS9cv0dxlxou26Q3liIxSQ=,tag:IENmJHOSX9nLhrnSVHZltg==,type:str] + lastmodified: "2024-03-17T20:41:27Z" + mac: ENC[AES256_GCM,data:f7RdcXpu9CGSZpIF8rwuIkn97EWRxJXxoC7KKbkZg4yxSxZJR/S5UXzEC56eY73IdBHap4op3l+cO7pT7p1rkspHQPH/5D225ihVQ8PQ29u2nlyyrrebB5tM1Mt+rJRlizBPxDDKySJYgdqZCWUwB8f5hQudpb0CGra7NfQreRg=,iv:vwpVqib7fyuV83FiyMT4BOeuqyrcspFyieQGWyZZzcU=,tag:zuJVSA2WqzSvM4MBWrdRlQ==,type:str] pgp: - - created_at: "2022-11-02T22:14:19Z" + - created_at: "2024-01-31T01:19:14Z" enc: |- -----BEGIN PGP MESSAGE----- - wV4D3ylLYNOsO+0SAQdASri/Ozm8ibaE1PN8ItRanuAGU4jRQL1g4U8GbsiXWzcw - u7trrk6foY98pfVAP4Z78X4Dp79UagorlDCT6F6yWtfFODFdTVJdbzJsD5QtZ1vK - 0lEBMmTyLDw4lzTpedDhvgkWpNd33TC3WgAfRb/2LCSPmoVp83O7ja6BfuBQDkWY - gP7g815fKYigaihDH8HlNzvRoOOcGC9+6lyQkHTJyRjKsrg= - =WfhH + hF4DY/xpNY5WhB0SAQdAkeQx8NatnRtZUJa/G0zaw+NL5twonTayNH8mmNBXOWgw + EWaC9Yq6yWntxxfkVaJHN5BEzxVVumrKmpKSIkvCkJqFZ5SuYH/DyE9oZZSr7iC/ + 0l4BTKZ8SdxQL8usQPSQVbs9skr7KsYfhtjTeTi823RwZLD1+wZKwqe43AJTE0Hl + b2jIihfXa7wKTfi9jXI/mpxLRpGH8kZnPoQuldkz1zWIU14YKoTKq55My8qwR4uW + =RazZ -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/hosts/aluminium/services/asterisk.nix b/hosts/aluminium/services/asterisk.nix deleted file mode 100644 index e3077d6..0000000 --- a/hosts/aluminium/services/asterisk.nix +++ /dev/null @@ -1,161 +0,0 @@ -{ config, lib, ... }: -let - secretConfigFiles = [ - "ari" - "pjsip" - "voicemail" - ]; - rtp = { - start = 10000; - end = 10200; - }; -in -{ - services.asterisk = { - enable = true; - confFiles = { - "extensions.conf" = '' - [sipgate-in] - exten = _499846876,1,Noop(Processing an incoming call) - ;same = n,Dial(PJSIP/10) - same = n,Dial(PJSIP/10&PJSIP/11,45,tT) - ;same = n,VoiceMail(876@lechner,u) - same = n,Hangup() - - exten => _4998469779781,1,Verbose(3,Incoming fax) - ; folder where your incoming faxes will initially be stored - same => n,Set(FAXDEST=/tmp) - - exten = _4998469779782,1,Noop(Test call) - same = n,Dial(PJSIP/10) - - ; put a timestamp on this call so the resulting file is unique - same => n,Set(tempfax=''${STRFTIME(,,%C%y%m%d%H%M)}) - same => n,ReceiveFax(''${FAXDEST}/''${tempfax}.tif) - same => n,Verbose(3,- Fax receipt completed with status: ''${FAXSTATUS}) - same => n,System(echo | s-nail -a ''${FAXDEST}/''${tempfax} -s "Asterisk Fax" mail@jalr.de) - - exten = _499846977892,1,Noop(Processing an incoming call) - same = n,Dial(PJSIP/12,30,tT) - ;same = n,VoiceMail(977892@pauline,u) - same = n,Hangup() - - exten = _499846977891,1,Noop(Processing an incoming call) - same = n,Dial(PJSIP/13&PJSIP/16,30,tT) - same = n,VoiceMail(977891@jakob,u) - same = n,Hangup() - - [dect] - exten = 100,1,Answer() - same = n,Wait(1) - same = n,Playback(hello-world) - same = n,Hangup() - - ;exten = 10,1,Dial(PJSIP/10,10) - ;same = n,VoiceMail(876@lechner,u) - - exten = _1X,1,Dial(PJSIP/''${EXTEN},30) - same = n,Hangup() - - exten = 50,1,Dial(PJSIP/10&PJSIP/11,30,tT) - same = n,Hangup() - - exten = 99,1,Answer() - same = n,VoiceMailMain(876@lechner) - - exten = 98,1,Answer() - same = n,VoiceMailMain(977892@pauline) - - exten = _ZX.,1,Noop(Ortsgespraech) - same = n,Dial(PJSIP/09846''${EXTEN}@sipgate,300) - same = n,Hangup() - - exten = _0ZXXXXX.,1,Noop(Deutsches Festnetz) - same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) - same = n,Hangup() - - exten = _00XXXXX.,1,Noop(Internationales Gespraech) - same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) - same = n,Hangup() - - [voicemail-callback] - exten = s,1,HasNewVoicemail(977892@pauline) - exten = s,2,Hangup - exten = s,102,Dial(PJSIP/12,15) - exten = s,n,VoiceMailMain(977892@pauline) - exten = s,n,Hangup - - - [fax-tx] - exten => s,1,NoOp(**** SENDING FAX ****) - ;exten => s,n,Set(FAXFILE=output.tif) - - exten => s,n,NoOp(**** SETTING FAXOPT ****) - exten => s,n,Set(FAXOPT(ecm)=yes) - ;exten => s,n,Set(FAXOPT(localstationid)=098469779780) - exten => s,n,Set(FAXOPT(maxrate)=14400) - exten => s,n,Set(FAXOPT(minrate)=2400) - exten => s,n,NoOp(**** SENDING FAX : ''${FAXFILE} ****) - exten => s,n,SendFAX(/var/lib/asterisk/fax-outgoing/''${FAXFILE},d) - - ;exten => send,n,Wait(6) - ;exten => send,n,Set(GLOBAL(FAXCOUNT)=''$[ ''${GLOBAL(FAXCOUNT)} + 1 ]) - ;exten => send,n,Set(FAXCOUNT=''${GLOBAL(FAXCOUNT)}) - ;exten => send,n,Set(FAXFILE=dw-faxout.tif) - ; Set FAXOPTs - ;exten => send,n,NoOp(**** SETTING FAXOPT ****) - ;exten => send,n,Set(FAXOPT(ecm)=yes) - ;exten => send,n,Set(FAXOPT(headerinfo)=Fax from ''${GLOBAL(LASTFAXCALLERNAME)} at ''${GLOBAL(LASTFAXCALLERNUM)} was received.) - ;exten => send,n,Set(FAXOPT(maxrate)=14400) - ; Send the fax - exten => send,n,SendFAX(/home/jalr/fax.tif,d) - ''; - "http.conf" = '' - [general] - enabled=yes - bindaddr=127.0.0.1 - - ; Port to bind to for HTTP sessions (default is 8088) - ;bindport=8088 - - tlsdisablev1=yes - tlsdisablev11=yes - tlsdisablev12=yes - - tlsservercipherorder=yes - ''; - "rtp.conf" = '' - [general] - rtpstart=${toString rtp.start} - rtpend=${toString rtp.end} - ''; - "dnsmgr.conf" = '' - [general] - enable=yes - refreshinterval=300 - ''; - }; - useTheseDefaultConfFiles = [ ]; - }; - - sops.secrets = (lib.listToAttrs (map - (name: lib.nameValuePair "asterisk-${name}" { - sopsFile = ../secrets.yaml; - owner = config.users.users.asterisk.name; - }) - secretConfigFiles)); - environment.etc = lib.mapAttrs' - (name: _: lib.nameValuePair - "asterisk/${name}.conf" - { source = config.sops.secrets."asterisk-${name}".path; }) - (lib.listToAttrs (map (name: lib.nameValuePair name { }) secretConfigFiles)); - - networking.firewall = { - allowedUDPPortRanges = [ - { - from = rtp.start; - to = rtp.end; - } - ]; - }; -} diff --git a/hosts/aluminium/services/asterisk/README.md b/hosts/aluminium/services/asterisk/README.md new file mode 100644 index 0000000..dc47981 --- /dev/null +++ b/hosts/aluminium/services/asterisk/README.md @@ -0,0 +1,9 @@ +## custom voicemail greetings + +Place `busy` and/or `unavail` file in users voicemail directory. + +The file can be converted to the fitting format using sox +```bash +sox $input_file -t wav -b 16 -c 1 -r 8k -e signed-integer --endian little unavail.wav +``` + diff --git a/hosts/aluminium/services/asterisk/default.nix b/hosts/aluminium/services/asterisk/default.nix new file mode 100644 index 0000000..463ac5e --- /dev/null +++ b/hosts/aluminium/services/asterisk/default.nix @@ -0,0 +1,267 @@ +{ config, lib, pkgs, ... }: + +let + inherit (config.networking) ports; + secretConfigFiles = [ + "ari" + "pjsip" + "voicemail" + ]; + voicemail-sounds = pkgs.callPackage ./voicemail-sounds { }; +in +{ + services.asterisk = { + enable = true; + confFiles = { + "extensions.conf" = '' + [doorbell] + exten = s,1,Set(__DYNAMIC_FEATURES=doorOpen) + same = n,Dial(PJSIP/10&PJSIP/11,${toString config.services.myintercom-doorbell.dialTime}) + same = n,Hangup() + + [door-open] + exten = s,1,Verbose(0, "opening the door") + same = n,System("${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-open-door") + same = n,Hangup() + + [sipgate-in] + exten = _499846876,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/10&PJSIP/11,25,tT) + same = n,VoiceMail(876@lechner,uS) + same = n,Hangup() + + exten => _4998469779781,1,Verbose(3,Incoming fax) + ; folder where your incoming faxes will initially be stored + same => n,Set(FAXDEST=/tmp) + + exten = _4998469779782,1,Noop(Test call) + same = n,Dial(PJSIP/10) + + ; put a timestamp on this call so the resulting file is unique + same => n,Set(tempfax=''${STRFTIME(,,%C%y%m%d%H%M)}) + same => n,ReceiveFax(''${FAXDEST}/''${tempfax}.tif) + same => n,Verbose(3,- Fax receipt completed with status: ''${FAXSTATUS}) + same => n,System(echo | s-nail -a ''${FAXDEST}/''${tempfax} -s "Asterisk Fax" mail@jalr.de) + + exten = _499846977892,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/12,30,tT) + ;same = n,VoiceMail(977892@pauline,u) + same = n,Hangup() + + exten = _499846977891,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/13&PJSIP/16,30,tT) + same = n,VoiceMail(977891@jakob,u) + same = n,Hangup() + + [dect] + exten = 100,1,Answer() + same = n,Wait(1) + same = n,Playback(hello-world) + same = n,Hangup() + + ;exten = 10,1,Dial(PJSIP/10,10) + ;same = n,VoiceMail(876@lechner,u) + + exten = _1X,1,Dial(PJSIP/''${EXTEN},30) + same = n,Hangup() + + exten = 50,1,Dial(PJSIP/10&PJSIP/11,30,tT) + same = n,Hangup() + + exten = 99,1,Answer() + same = n,VoiceMailMain(876@lechner) + + exten = 98,1,Answer() + same = n,VoiceMailMain(977892@pauline) + + exten = _ZX.,1,Noop(Ortsgespraech) + same = n,Dial(PJSIP/09846''${EXTEN}@sipgate,300) + same = n,Hangup() + + exten = _0ZXXXXX.,1,Noop(Deutsches Festnetz) + same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) + same = n,Hangup() + + exten = _00XXXXX.,1,Noop(Internationales Gespraech) + same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) + same = n,Hangup() + + [voicemail-callback] + exten = s,1,HasNewVoicemail(876@lechner) + exten = s,2,Hangup + exten = s,102,Dial(PJSIP/10&PJSIP/11,15) + exten = s,n,VoiceMailMain(876@lechner) + exten = s,n,Hangup + + + [fax-tx] + exten => s,1,NoOp(**** SENDING FAX ****) + ;exten => s,n,Set(FAXFILE=output.tif) + + exten => s,n,NoOp(**** SETTING FAXOPT ****) + exten => s,n,Set(FAXOPT(ecm)=yes) + ;exten => s,n,Set(FAXOPT(localstationid)=098469779780) + exten => s,n,Set(FAXOPT(maxrate)=14400) + exten => s,n,Set(FAXOPT(minrate)=2400) + exten => s,n,NoOp(**** SENDING FAX : ''${FAXFILE} ****) + exten => s,n,SendFAX(/var/lib/asterisk/fax-outgoing/''${FAXFILE},d) + + ;exten => send,n,Wait(6) + ;exten => send,n,Set(GLOBAL(FAXCOUNT)=''$[ ''${GLOBAL(FAXCOUNT)} + 1 ]) + ;exten => send,n,Set(FAXCOUNT=''${GLOBAL(FAXCOUNT)}) + ;exten => send,n,Set(FAXFILE=dw-faxout.tif) + ; Set FAXOPTs + ;exten => send,n,NoOp(**** SETTING FAXOPT ****) + ;exten => send,n,Set(FAXOPT(ecm)=yes) + ;exten => send,n,Set(FAXOPT(headerinfo)=Fax from ''${GLOBAL(LASTFAXCALLERNAME)} at ''${GLOBAL(LASTFAXCALLERNUM)} was received.) + ;exten => send,n,Set(FAXOPT(maxrate)=14400) + ; Send the fax + exten => send,n,SendFAX(/home/jalr/fax.tif,d) + ''; + "features.conf" = '' + [applicationmap] + doorOpen => 1,peer,Gosub,"door-open,s,1" + ''; + "http.conf" = '' + [general] + enabled=yes + bindaddr=127.0.0.1 + + ; Port to bind to for HTTP sessions (default is 8088) + ;bindport=8088 + + tlsdisablev1=yes + tlsdisablev11=yes + tlsdisablev12=yes + + tlsservercipherorder=yes + ''; + "rtp.conf" = '' + [general] + rtpstart=${toString ports.asterisk-rtp.udp.from} + rtpend=${toString ports.asterisk-rtp.udp.to} + ''; + "dnsmgr.conf" = '' + [general] + enable=yes + refreshinterval=300 + ''; + }; + useTheseDefaultConfFiles = [ ]; + }; + + sops.secrets = lib.listToAttrs (map + (name: lib.nameValuePair "asterisk-${name}" { + owner = config.users.users.asterisk.name; + }) + secretConfigFiles); + environment.etc = lib.mapAttrs' + (name: _: lib.nameValuePair + "asterisk/${name}.conf" + { source = config.sops.secrets."asterisk-${name}".path; }) + (lib.listToAttrs (map (name: lib.nameValuePair name { }) secretConfigFiles)); + + networking.firewall = { + allowedUDPPortRanges = lib.singleton ports.asterisk-rtp.udp; + interfaces.voice = { + allowedTCPPorts = [ 5060 ]; + allowedUDPPorts = [ 5060 ]; + }; + }; + + systemd.services = { + "asterisk-reload-endpoint@" = { + description = "Check if asterisk endpoint is identified and reload it when it is not."; + serviceConfig = { + Type = "oneshot"; + }; + environment = { + ENDPOINT = "%I"; + }; + script = '' + export PATH=${pkgs.lib.makeBinPath [pkgs.asterisk pkgs.gnused pkgs.gnugrep]} + if ! asterisk -r -x "pjsip show endpoint $ENDPOINT" | sed -n '/^===/,/^\s*ParameterName/{//!p}' | grep -q 'Identify:'; then + asterisk -r -x "module reload res_pjsip_endpoint_identifier_ip.so" + fi + ''; + }; + asterisk-voicemail-sounds = { + wantedBy = [ "asterisk.service" ]; + after = [ "asterisk.service" ]; + script = '' + ln -sfn \ + ${voicemail-sounds}/unavail.wav \ + /var/spool/asterisk/voicemail/lechner/876/unavail.wav + ''; + restartTriggers = [ voicemail-sounds ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + "asterisk-voicemail-call@" = { + description = "Check if voicemail exists and place a call to the voicemail application."; + serviceConfig = { + Type = "oneshot"; + }; + scriptArgs = "%I"; + script = '' + export PATH=${pkgs.lib.makeBinPath [pkgs.asterisk pkgs.coreutils pkgs.findutils]} + number="$(echo "$1" | cut -d ':' -f 1)" + user="$(echo "$1" | cut -d ':' -f 2)" + channel="PJSIP/$(echo "$1" | cut -d ':' -f 3)" + + if ! find "/var/spool/asterisk/voicemail/$user/$number/INBOX/" -mindepth 1 -maxdepth 1 | read; then + exit + fi + + callfile="$(mktemp -p /tmp XXXXXXXXXX.call)" + chmod 644 "$callfile" + + cat > "$callfile" << EOF + Channel: $channel + WaitTime: 15 + Application: VoiceMailMain + Data: $number@$user + CallerID: Voicemail + EOF + + mv "$callfile" /var/spool/asterisk/outgoing/ + ''; + }; + }; + + systemd.timers = { + asterisk-reload-endpoint = { + description = "Check if asterisk endpoint is identified and reload it when it is not."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* *:*:00"; + Unit = "asterisk-reload-endpoint@sipgate.service"; + }; + }; + + asterisk-voicemail-call-10 = { + description = "Check if voicemail exists and place a call to the voicemail application."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 07..22:00,20,40:00"; + Unit = "asterisk-voicemail-call@876:lechner:10.service"; + }; + }; + asterisk-voicemail-call-11 = { + description = "Check if voicemail exists and place a call to the voicemail application."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 07..22:00,10,30:50"; + Unit = "asterisk-voicemail-call@876:lechner:11.service"; + }; + }; + }; +} diff --git a/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix b/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix new file mode 100644 index 0000000..06451f1 --- /dev/null +++ b/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix @@ -0,0 +1,13 @@ +{ stdenvNoCC }: + +stdenvNoCC.mkDerivation { + name = "voicemail-sounds"; + + src = ./.; + + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; +} diff --git a/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav b/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav new file mode 100644 index 0000000..35b972e --- /dev/null +++ b/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9d91a693e1343a1dfdbb1e4adc89a5f58182d5c1427254f0976fa09120c9091 +size 149444 diff --git a/hosts/aluminium/services/default.nix b/hosts/aluminium/services/default.nix index 1ab0528..8639a37 100644 --- a/hosts/aluminium/services/default.nix +++ b/hosts/aluminium/services/default.nix @@ -1,8 +1,13 @@ { imports = [ - ./asterisk.nix + ./asterisk ./dnsmasq.nix + ./doorbell.nix ./dyndns.nix + ./esphome + ./home-assistant.nix + ./nginx.nix + ./ntp.nix ./unifi-controller.nix ]; } diff --git a/hosts/aluminium/services/dnsmasq.nix b/hosts/aluminium/services/dnsmasq.nix index 9040024..c6ada0d 100644 --- a/hosts/aluminium/services/dnsmasq.nix +++ b/hosts/aluminium/services/dnsmasq.nix @@ -1,8 +1,5 @@ -{ pkgs, ... }: +{ lib, pkgs, ... }: -let - stateDir = "/var/lib/dnsmasq"; -in { services.dnsmasq = { enable = true; @@ -10,13 +7,23 @@ in listen-address = [ "192.168.0.1" "192.168.1.1" + "192.168.2.1" + "192.168.10.9" ]; interface = "lo"; expand-hosts = true; - domain = "lan.kbh.jalr.de"; + domain = [ + "lan.kbh.jalr.de" + "iot.kbh.jalr.de,192.168.2.0/24" + ]; dhcp-range = [ "192.168.0.20,192.168.0.254,4h" "192.168.1.20,192.168.1.254,4h" + "192.168.2.20,192.168.2.254,4h" + "192.168.10.8,static,24h" + ]; + dhcp-host = [ + "AC:CC:8E:40:1C:B9,192.168.10.10,sprechanlage,infinite" ]; cache-size = 10000; dns-forward-max = 1000; @@ -29,11 +36,24 @@ in "2001:470:20::2" # ordns.he.net "74.82.42.42" # ordns.he.net ]; + dhcp-option = [ + "option:ntp-server,192.168.0.1" + ]; }; }; - networking.firewall = { - allowedUDPPorts = [ 53 67 ]; - allowedTCPPorts = [ 53 ]; - }; + networking.firewall.interfaces = lib.attrsets.genAttrs [ + "heizung" + "iot" + "lechner" + "pv" + "sprechanlage" + "voice" + ] + ( + _: { + allowedUDPPorts = [ 53 67 ]; + allowedTCPPorts = [ 53 ]; + } + ); } diff --git a/hosts/aluminium/services/doorbell.nix b/hosts/aluminium/services/doorbell.nix new file mode 100644 index 0000000..01190f4 --- /dev/null +++ b/hosts/aluminium/services/doorbell.nix @@ -0,0 +1,30 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + sops.secrets.myintercom-doorbell-password.owner = "asterisk"; + services.myintercom-doorbell = { + enable = true; + host = "sprechanlage.lan.kbh.jalr.de"; + username = "btxpvt0002"; + passwordFile = config.sops.secrets.myintercom-doorbell-password.path; + audiosocket = { + address = "127.0.0.1"; + port = ports.doorbell-audiosocket.tcp; + uuid = "4960ab41-dbef-4773-a25e-90536d97345e"; + }; + callerId = "Sprechanlage"; + cam = { + enable = true; + bindAddress = "192.168.0.1"; + webrtcPort = ports.doorbell-webrtc.tcp; + webrtcIceTcpPort = ports.doorbell-webrtc-ice.tcp; + }; + }; + networking.firewall.interfaces.lechner.allowedTCPPorts = [ + ports.doorbell-webrtc.tcp + ports.doorbell-webrtc-ice.tcp + ]; +} diff --git a/hosts/aluminium/services/dyndns.nix b/hosts/aluminium/services/dyndns.nix index cfca934..546cb07 100644 --- a/hosts/aluminium/services/dyndns.nix +++ b/hosts/aluminium/services/dyndns.nix @@ -1,16 +1,42 @@ -{ config, ... }: +{ config, lib, pkgs, ... }: +let + mkService = config: + lib.mapAttrs' + (name: cfg: lib.nameValuePair "godns-${name}" ( + let + config = cfg.settings // { + login_token_file = "$CREDENTIALS_DIRECTORY/login_token"; + }; + configFile = (pkgs.formats.yaml { }).generate "config.yaml" config; + in + { + description = "GoDNS service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${lib.getExe pkgs.godns} -c ${configFile}"; + LoadCredential = "login_token:${cfg.tokenPath}"; + Restart = "always"; + RestartSec = "2s"; + }; + } + )) + config; +in { - sops.secrets.duckdns-secret = { - sopsFile = ../secrets.yaml; - }; - services.ddclient = { - enable = true; - interval = "1min"; - protocol = "duckdns"; - server = "www.duckdns.org"; - username = "nouser"; - passwordFile = config.sops.secrets.duckdns-secret.path; - domains = [ "jalr-k" ]; - ipv6 = false; + systemd.services = mkService { + ip4 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-k" ]; }]; + resolver = "8.8.8.8"; + ip_interface = "ppp0"; + ip_urls = [ "" ]; + ip_type = "IPv4"; + interval = 60; + }; + }; }; } diff --git a/hosts/aluminium/services/esphome/default.nix b/hosts/aluminium/services/esphome/default.nix new file mode 100644 index 0000000..4e63710 --- /dev/null +++ b/hosts/aluminium/services/esphome/default.nix @@ -0,0 +1,25 @@ +{ pkgs +, config +, ... +}: +let + inherit (config.networking) ports; +in +{ + sops.secrets.esphome.restartUnits = [ config.systemd.services.esphome.name ]; + + jalr.esphome = { + enable = true; + port = ports.esphome.tcp; + secretsFile = config.sops.secrets.esphome.path; + configDir = pkgs.stdenvNoCC.mkDerivation { + name = "esphome-config"; + src = ./devices; + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; + }; + }; +} diff --git a/hosts/aluminium/services/esphome/devices/.env b/hosts/aluminium/services/esphome/devices/.env new file mode 100644 index 0000000..640bd9c --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/.env @@ -0,0 +1,2 @@ +ESPHOME_HOST="jalr-k.duckdns.org" +ESPHOME_SECRETS_FILE="esphome_${ESPHOME_HOST}_secrets.yaml" diff --git a/hosts/aluminium/services/esphome/devices/.gitignore b/hosts/aluminium/services/esphome/devices/.gitignore new file mode 100644 index 0000000..d8b4157 --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/hosts/aluminium/services/esphome/devices/justfile b/hosts/aluminium/services/esphome/devices/justfile new file mode 120000 index 0000000..68d1c45 --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/justfile @@ -0,0 +1 @@ +../../../../../modules/esphome/devices/justfile \ No newline at end of file diff --git a/hosts/aluminium/services/esphome/devices/wasserbett.yaml b/hosts/aluminium/services/esphome/devices/wasserbett.yaml new file mode 100644 index 0000000..8d0e6ab --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/wasserbett.yaml @@ -0,0 +1,64 @@ +esphome: + name: "waterbed" + friendly_name: "Wasserbett" + +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +api: + encryption: + key: !secret apikey_waterbed + +ota: + - platform: esphome + password: !secret otapass_waterbed + +wifi: + ssid: !secret wifi_ssid_kbh + password: !secret wifi_password_kbh + domain: .iot.kbh.jalr.de + enable_on_boot: true + fast_connect: true + +switch: + - platform: gpio + pin: + number: 13 + id: pump + icon: "mdi:electric-switch" + +dallas: + - pin: 12 + +sensor: + - platform: dallas + #address: 0xb7000802397ccc10 + index: 0 + name: "Temperatur" + id: temperature_waterbed + +climate: + - platform: thermostat + name: "Temperatur" + id: temperature + sensor: temperature_waterbed + heat_deadband: 0.2 + heat_overrun: 0.2 + min_heating_off_time: 300s + min_heating_run_time: 300s + min_idle_time: 30s + heat_action: + - switch.turn_on: pump + idle_action: + - switch.turn_off: pump + default_preset: heizen + on_boot_restore_from: memory + preset: + - name: heizen + default_target_temperature_low: 28.5 °C + - name: abwesend + default_target_temperature_low: 24 °C diff --git a/hosts/aluminium/services/home-assistant.nix b/hosts/aluminium/services/home-assistant.nix new file mode 100644 index 0000000..633b210 --- /dev/null +++ b/hosts/aluminium/services/home-assistant.nix @@ -0,0 +1,141 @@ +{ pkgs, config, ... }: +let + inherit (config.networking) ports; +in +{ + services.home-assistant = { + enable = true; + lovelaceConfig = { + title = "Home"; + views = [ + { + path = "default_view"; + title = "Home"; + cards = [ + { + title = "Heizung"; + type = "entities"; + entities = [ + { entity = "sensor.guntamaticbiostar_betrieb"; } + { entity = "sensor.guntamaticbiostar_pufferladung"; } + { entity = "sensor.guntamaticbiostar_puffer_oben"; } + { entity = "sensor.guntamaticbiostar_puffer_unten"; } + { entity = "sensor.guntamaticbiostar_kesseltemperatur"; } + { entity = "sensor.guntamaticbiostar_vorlauf_ist_1"; } + { entity = "sensor.guntamaticbiostar_aussentemperatur"; } + { entity = "sensor.guntamaticbiostar_co2_gehalt"; } + { entity = "select.guntamaticbiostar_program"; } + { entity = "sensor.guntamaticbiostar_programm"; } + { entity = "sensor.guntamaticbiostar_programm_hk1"; } + { entity = "sensor.guntamaticbiostar_rucklauftemperatur"; } + { entity = "sensor.guntamaticbiostar_servicezeit"; } + ]; + } + { + type = "grid"; + square = false; + columns = 1; + cards = [ + { + title = "Wasserbett"; + type = "entities"; + entities = [ + { + entity = "sensor.waterbed_temperatur"; + name = "Temperatur"; + } + ]; + } + { + type = "thermostat"; + entity = "climate.waterbed_temperatur"; + } + ]; + } + ]; + } + ]; + }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "esphome" + "openweathermap" + ]; + customComponents = [ + # https://github.com/a529987659852/GuntamaticBiostar + pkgs.home-assistant-custom-components.guntamatic + ]; + lovelaceConfigWritable = false; + configWritable = false; + config = { + http = { + server_host = [ "127.0.0.1" ]; + server_port = ports.home-assistant.tcp; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + homeassistant = { + unit_system = "metric"; + time_zone = "Europe/Berlin"; + temperature_unit = "C"; + inherit (config.location) longitude; + inherit (config.location) latitude; + }; + default_config = { }; + "automation nix" = [ + { + alias = "Nachschüren"; + description = "Benachrichtigung auf iPad bei Wechsel auf Teillast"; + mode = "single"; + trigger = [ + { + platform = "state"; + entity_id = [ "sensor.guntamaticbiostar_betrieb" ]; + from = "VOLLLAST"; + to = "TEILLAST"; + } + ]; + condition = [ + { + condition = "numeric_state"; + entity_id = "sensor.guntamaticbiostar_pufferladung"; + below = "80"; + } + ]; + action = [ + { + device_id = "5612874405fa2ee539ad4518a1bb8e34"; + domain = "mobile_app"; + type = "notify"; + message = '' + Kessel läuft auf Teillast und Puffer ist unter 80%. Vielleicht willst du + nachschüren. + ''; + title = "Nachschüren?"; + } + ]; + } + ]; + "automation ui" = "!include automations.yaml"; + "scene nix" = [ + ]; + "scene ui" = "!include scenes.yaml"; + }; + }; + + systemd.tmpfiles.rules = [ + "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" + "f ${config.services.home-assistant.configDir}/scenes.yaml 0755 hass hass" + ]; + + services.nginx.virtualHosts."hass.kbh.jalr.de" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.home-assistant.tcp}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/aluminium/services/nginx.nix b/hosts/aluminium/services/nginx.nix new file mode 100644 index 0000000..6eb78ee --- /dev/null +++ b/hosts/aluminium/services/nginx.nix @@ -0,0 +1,20 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + services.nginx = { + enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; +} diff --git a/hosts/aluminium/services/ntp.nix b/hosts/aluminium/services/ntp.nix new file mode 100644 index 0000000..45917cf --- /dev/null +++ b/hosts/aluminium/services/ntp.nix @@ -0,0 +1,12 @@ +{ + services.chrony = { + enable = true; + extraConfig = '' + allow 192.168.0.0/24 + allow 192.168.10.0/24 + leapsectz right/UTC + ''; + }; + networking.firewall.interfaces.lechner.allowedUDPPorts = [ 123 ]; + networking.firewall.interfaces.heizung.allowedUDPPorts = [ 123 ]; +} diff --git a/hosts/aluminium/services/unifi-controller.nix b/hosts/aluminium/services/unifi-controller.nix index f34d12d..ae39578 100644 --- a/hosts/aluminium/services/unifi-controller.nix +++ b/hosts/aluminium/services/unifi-controller.nix @@ -1,9 +1,16 @@ -{ pkgs, ... }: +{ config, pkgs, ... }: + +let + inherit (config.networking) ports; +in { services.unifi = { enable = true; - openFirewall = true; unifiPackage = pkgs.unifi; + mongodbPackage = pkgs.mongodb-7_0; }; - networking.firewall.allowedTCPPorts = [ 8443 ]; + networking.firewall.interfaces.lechner.allowedTCPPorts = [ + ports.unifi-inform.tcp + ports.unifi-ui.tcp + ]; } diff --git a/hosts/cadmium/configuration.nix b/hosts/cadmium/configuration.nix index 01e405d..32c1151 100644 --- a/hosts/cadmium/configuration.nix +++ b/hosts/cadmium/configuration.nix @@ -1,16 +1,13 @@ -{ config, pkgs, ... }: +{ pkgs, ... }: { imports = [ ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ../../users/jalr ]; networking = { hostName = "cadmium"; - networkmanager = { - enable = true; - }; useDHCP = false; firewall = { @@ -37,10 +34,6 @@ programs.mtr.enable = true; - hardware.bluetooth.enable = true; - services.blueman.enable = true; - services.ofono.enable = true; - services.udisks2.enable = true; # udevadm info --name /dev/foo --query all @@ -52,10 +45,13 @@ jalr = { bootloader = "systemd-boot"; + bluetooth.enable = true; uefi.enable = true; - gui.enable = true; + gui = { + enable = true; + sway.enable = true; + }; workstation.enable = true; - sdr.enable = true; libvirt.enable = true; autologin.enable = true; autologin.username = "jalr"; @@ -67,6 +63,6 @@ # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + system.stateVersion = "23.11"; # Did you read the comment? } diff --git a/hosts/cadmium/hardware-configuration.nix b/hosts/cadmium/hardware-configuration.nix index ba6a7f2..1a85863 100644 --- a/hosts/cadmium/hardware-configuration.nix +++ b/hosts/cadmium/hardware-configuration.nix @@ -42,11 +42,13 @@ "/boot" = { device = "/dev/disk/by-uuid/D384-54D8"; fsType = "vfat"; + options = [ "nodev" "nosuid" "noexec" ]; }; "/home" = { device = "/dev/disk/by-uuid/f14ae966-ac3f-467f-9263-ba9136967782"; fsType = "ext4"; noCheck = true; + options = [ "nodev" "nosuid" ]; }; }; diff --git a/hosts/copper/configuration.nix b/hosts/copper/configuration.nix new file mode 100644 index 0000000..841d390 --- /dev/null +++ b/hosts/copper/configuration.nix @@ -0,0 +1,77 @@ +{ lib, ... }: + +{ + imports = [ + ./hardware-configuration.nix + ./disko.nix + ../../users/jalr + ./services + ./framework-fixes.nix + ]; + + networking = { + hostName = "copper"; + extraHosts = lib.concatStringsSep "\n" ( + lib.attrsets.mapAttrsToList + (addr: hosts: + lib.concatStringsSep " " ([ addr ] ++ hosts) + ) + { + #"192.0.2.1" = ["example.com"]; + } + ); + firewall.interfaces.virbr0.allowedTCPPorts = [ 53 64172 ]; + firewall.interfaces.virbr0.allowedUDPPorts = [ 53 67 69 4011 ]; + }; + + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; + }; + + services = { + fstrim.enable = true; + flatpak.enable = true; + snapper.configs = { + home = { + SUBVOLUME = "/home"; + ALLOW_USERS = [ "jalr" ]; + TIMELINE_CREATE = true; + TIMELINE_CLEANUP = true; + TIMELINE_LIMIT_HOURLY = 12; + TIMELINE_LIMIT_DAILY = 7; + TIMELINE_LIMIT_WEEKLY = 4; + TIMELINE_LIMIT_MONTHLY = 3; + TIMELINE_LIMIT_YEARLY = 0; + BACKGROUND_COMPARISON = "yes"; + NUMBER_CLEANUP = "no"; + NUMBER_MIN_AGE = "1800"; + NUMBER_LIMIT = "100"; + NUMBER_LIMIT_IMPORTANT = "10"; + EMPTY_PRE_POST_CLEANUP = "yes"; + EMPTY_PRE_POST_MIN_AGE = "1800"; + }; + }; + }; + + jalr = { + bootloader = "lanzaboote"; + bluetooth.enable = true; + uefi.enable = true; + gui = { + enable = true; + sway.enable = true; + }; + workstation.enable = true; + libvirt.enable = true; + autologin = { + enable = true; + username = "jalr"; + }; + }; + + system.stateVersion = "24.05"; +} + diff --git a/hosts/copper/disko.nix b/hosts/copper/disko.nix new file mode 100644 index 0000000..bdbbf17 --- /dev/null +++ b/hosts/copper/disko.nix @@ -0,0 +1,59 @@ +{ + disko.devices = { + disk = { + nvme = { + type = "disk"; + device = "/dev/disk/by-id/nvme-Samsung_SSD_990_PRO_2TB_S7DNNJ0X235226N"; + content = { + type = "gpt"; + partitions = { + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + luks = { + size = "100%"; + content = { + type = "luks"; + name = "copper-crypt"; + settings = { + allowDiscards = true; + }; + extraFormatArgs = [ "--hash sha512 --use-random --pbkdf argon2id --iter-time 5000 --pbkdf-memory ${builtins.toString (4*1024*1024)} --pbkdf-parallel 4" ]; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/home/.snapshots" = { + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "noatime" "nodev" ]; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} + + diff --git a/hosts/copper/framework-fixes.nix b/hosts/copper/framework-fixes.nix new file mode 100644 index 0000000..f1463d2 --- /dev/null +++ b/hosts/copper/framework-fixes.nix @@ -0,0 +1,14 @@ +{ pkgs, ... }: + +{ + boot.extraModprobeConfig = '' + options cfg80211 ieee80211_regdom="DE" + options mt7921_common disable_clc=1 + options mt7921e disable_aspm=Y + ''; + hardware.firmware = [ pkgs.wireless-regdb ]; + + services.udev.extraRules = '' + ACTION=="add", SUBSYSTEM=="pci", ATTR{power/wakeup}="disabled" + ''; +} diff --git a/hosts/copper/hardware-configuration.nix b/hosts/copper/hardware-configuration.nix new file mode 100644 index 0000000..aa46864 --- /dev/null +++ b/hosts/copper/hardware-configuration.nix @@ -0,0 +1,18 @@ +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd = { + availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ]; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + environment.systemPackages = with pkgs; [ + fw-ectool + ]; +} diff --git a/hosts/copper/secrets.yaml b/hosts/copper/secrets.yaml new file mode 100644 index 0000000..5ad148c --- /dev/null +++ b/hosts/copper/secrets.yaml @@ -0,0 +1,32 @@ +ntfy_shiftphone: ENC[AES256_GCM,data:WG/LlELNgEh2BiyrOYLDvYk3AlObSvUYUH8v3Cq9oHOhN1+Iwg==,iv:MVwLBIQjY8Z31V9mXf7Ge/jGb9S7ceLFx2TffcsO+o4=,tag:skeQbBPLYH8D4CPDorJ0fQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1rrut5ntrkqmvttvmpa5jcmjhr2pfpyaqgu9dmtx6v07lgjxx5ppsl7e5v3 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzbXFqbHJFM0xxL284dWZD + TDkzcGVSRGorRWQvV3h3dkJ6UjNOeUxVcGdRCk5jTkZDeVFORVVWdm1vZm5XUHdk + S0ZBTEdEeDgramZNZm5xK3RkVkkxSDgKLS0tIFZ6dysvVm1YNlJzOVFXZXhrdXBE + dU0reGFSUmRxb0ZlUHgyYlpjU0FOQUEKuOMKvkZcynBGyMHmAYmz13Jy32YKyVK0 + ztCWcXbl9qCe6KtI0yW+t8DLk/PaRrmSrB+2ICTMFqPh7HiBoX+KgQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-03-12T20:36:21Z" + mac: ENC[AES256_GCM,data:BpwQmtqj8NkTNO7cJHMoOeILY4HRcb7OasiCcnXsBwIFvbeDgwj+DMZOeKbitLXwzS5frWhZWg0eBHQ4BZQFjX1K0KReVacH9CblHnSZLxjMg3x6o3upB70YjdmD3KKBisOwfMCjklwk0rKwx0w5vzac3r1nJU+PGtFw1luIiBs=,iv:bYIRVFWVGjwgmaGu6JqvpCa0TIp8idP5Bc5cYV7Bri8=,tag:D2xS1PK9a9Dd1mm8+R9RRA==,type:str] + pgp: + - created_at: "2025-03-12T20:51:07Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4DY/xpNY5WhB0SAQdAhB2C4sQhoL04j1RiWoeNCNSbGxDkrqXP+IffdoY8DWgw + x8aogh0b7CpTplBG/4g/WMVB4N/86uvI+mLYxJMyRb9b0f0bDr5dPpnhk//r/MDg + 0l4B9+hcSzmkwXlKh7L8Ds4cZr/z3RlqnR424KSfKbiaaigYttui5l4xgEEPZE1H + 1yfIJ5lBMgG1HTj3HX5mqM9ocA4HVzIkfPPqrFRAgjZdqeDEbLBT3lItMlvsOwy4 + =kS0b + -----END PGP MESSAGE----- + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 + unencrypted_suffix: _unencrypted + version: 3.9.4 diff --git a/hosts/copper/services/default.nix b/hosts/copper/services/default.nix new file mode 100644 index 0000000..663c3a8 --- /dev/null +++ b/hosts/copper/services/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./illuminanced.nix + ./ntfy.nix + ./timelog.nix + ./webdev.nix + ]; +} diff --git a/hosts/copper/services/illuminanced.nix b/hosts/copper/services/illuminanced.nix new file mode 100644 index 0000000..dde4c64 --- /dev/null +++ b/hosts/copper/services/illuminanced.nix @@ -0,0 +1,94 @@ +{ lib, pkgs, ... }: + +let + tomlFormat = pkgs.formats.toml { }; + cfg = { + daemonize = { + log_to = "syslog"; + pid_file = "/run/illuminanced/illuminanced.pid"; + #log_level = "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" + log_level = "ERROR"; + }; + general = { + check_period_in_seconds = 1; + light_steps = 100; + min_backlight = 20; + step_barrier = 0.1; + max_backlight_file = "/sys/class/backlight/amdgpu_bl1/max_brightness"; + backlight_file = "/sys/class/backlight/amdgpu_bl1/brightness"; + illuminance_file = "/sys/bus/iio/devices/iio:device0/in_illuminance_raw"; + #event_device_mask = "/dev/input/event*"; + #event_device_name = "Asus WMI hotkeys"; + enable_max_brightness_mode = true; + filename_for_sensor_activation = ""; + }; + kalman = { + q = 1; + r = 20; + covariance = 10; + }; + light = { + points_count = 6; + + illuminance_0 = 0; + light_0 = 0; + illuminance_1 = 20; + light_1 = 35; + illuminance_2 = 70; + light_2 = 50; + illuminance_3 = 120; + light_3 = 65; + illuminance_4 = 200; + light_4 = 75; + illuminance_5 = 255; + light_5 = 99; + }; + }; + configFile = tomlFormat.generate "illuminanced.toml" cfg; +in +{ + systemd.services.illuminanced = { + description = "Ambient Light Sensor Daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "exec"; + Restart = "always"; + ExecStart = "${pkgs.illuminanced}/bin/illuminanced -c ${configFile}"; + PIDFile = cfg.daemonize.pid_file; + StandardOutput = "journal"; + BindReadOnlyPaths = [ + "/nix/store" + "/dev/log" + "/run/systemd/journal/socket" + "/run/systemd/journal/stdout" + cfg.general.max_backlight_file + (lib.strings.escape [ ":" ] cfg.general.illuminance_file) + ]; + BindPaths = [ + cfg.general.backlight_file + ]; + CapabilityBoundingSet = null; + IPAddressDeny = "any"; + LockPersonality = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RootDirectory = "/run/illuminanced"; + RuntimeDirectory = "illuminanced"; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + }; + }; +} diff --git a/hosts/copper/services/ntfy.nix b/hosts/copper/services/ntfy.nix new file mode 100644 index 0000000..e3e7665 --- /dev/null +++ b/hosts/copper/services/ntfy.nix @@ -0,0 +1,3 @@ +{ + sops.secrets.ntfy_shiftphone.owner = "jalr"; +} diff --git a/hosts/copper/services/timelog.nix b/hosts/copper/services/timelog.nix new file mode 100644 index 0000000..3343897 --- /dev/null +++ b/hosts/copper/services/timelog.nix @@ -0,0 +1,10 @@ +{ + powerManagement = { + powerUpCommands = '' + echo "timelog: powerUp" + ''; + powerDownCommands = '' + echo "timelog: powerDown" + ''; + }; +} diff --git a/hosts/copper/services/webdev.nix b/hosts/copper/services/webdev.nix new file mode 100644 index 0000000..5236f67 --- /dev/null +++ b/hosts/copper/services/webdev.nix @@ -0,0 +1,50 @@ +{ pkgs, lib, ... }: +{ + systemd.services = lib.attrsets.mapAttrs' + ( + name: mapping: lib.attrsets.nameValuePair "redir-${name}" { + description = "Port redirection for local development web server (${name})"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + BindReadOnlyPaths = [ "/nix/store" ]; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + ExecStart = "${pkgs.redir}/bin/redir -n 127.0.0.1:${toString mapping.to} 127.0.0.1:${toString mapping.from}"; + IPAddressAllow = "localhost"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + ReadWritePaths = ""; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/redir-https"; + RuntimeDirectory = "redir-https"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + Type = "exec"; + }; + } + ) + { + http = { from = 8080; to = 80; }; + https = { from = 8443; to = 443; }; + }; +} diff --git a/hosts/default.nix b/hosts/default.nix index 322731b..357302f 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -1,7 +1,10 @@ -{ ... }@inputs: +inputs: +let + hardware = inputs.nixos-hardware.nixosModules; +in { aluminium = { - targetHost = "192.168.0.1"; + targetHost = "jalr-k.duckdns.org"; system = "x86_64-linux"; }; jalr-t520 = { @@ -10,20 +13,20 @@ cadmium = { system = "x86_64-linux"; }; - hafnium = { - system = "x86_64-linux"; - }; - weinturm-pretix-prod = { - system = "aarch64"; - targetHost = "142.132.185.70"; - }; iron = { system = "x86_64-linux"; #targetHost = "192.168.42.1"; targetHost = "jalr-bw.duckdns.org"; }; magnesium = { - system = "aarch64"; - targetHost = "162.55.35.199"; + system = "x86_64-linux"; + targetHost = "magnesium.jalr.de"; + }; + copper = { + system = "x86_64-linux"; + targetHost = "copper.lan.bw.jalr.de"; + extraModules = [ + hardware.framework-16-7040-amd + ]; }; } diff --git a/hosts/hafnium/configuration.nix b/hosts/hafnium/configuration.nix deleted file mode 100644 index 76cf283..0000000 --- a/hosts/hafnium/configuration.nix +++ /dev/null @@ -1,142 +0,0 @@ -{ lib, config, pkgs, self, system, ... }: - -{ - imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jal.nix - ]; - - networking = { - hostName = "hafnium"; - networkmanager = { - enable = true; - }; - useDHCP = false; - interfaces = { - enp2s0f0.useDHCP = false; - enp5s0.useDHCP = false; - wlp3s0.useDHCP = false; - }; - firewall = { - allowedUDPPorts = [ - 53 - ]; - allowedTCPPorts = [ - 53 - ]; - }; - extraHosts = '' - #10.10.10.10 example.com - ''; - }; - - environment.systemPackages = with pkgs; [ - brightnessctl - gnome3.adwaita-icon-theme - openconnect - redir - tcpdump - ]; - - environment.variables.EDITOR = "nvim"; - - programs.mtr.enable = true; - - hardware.bluetooth.enable = true; - services.blueman.enable = true; - services.ofono.enable = true; - - services.udisks2.enable = true; - - jalr = { - bootloader = "systemd-boot"; - uefi.enable = true; - gui.enable = true; - workstation.enable = true; - sdr.enable = false; - libvirt.enable = true; - autologin.enable = true; - autologin.username = "jal"; - tradebyte.enable = true; - }; - - - sops.secrets = ( - lib.listToAttrs (map - (name: lib.nameValuePair "wireguard_key_${name}" { - sopsFile = ./secrets.yaml; - }) - [ - "tbcore" - "ops-testing" - ] - ) - ); - - networking.wireguard.interfaces = { - tbcore = { - ips = [ "172.27.27.16/32" ]; - privateKeyFile = config.sops.secrets.wireguard_key_tbcore.path; - listenPort = 51930; - - peers = [{ - publicKey = "K5vF/yTag6NnWjZsMug63DERdCFRfHoqxVkgKH55oFE="; - endpoint = "194.33.184.175:51930"; - #endpoint = "ccs-emergency-vpn.core.tradebyte.com:51930"; - persistentKeepalive = 25; - allowedIPs = [ - "10.158.128.0/23" - "10.158.224.0/20" - "10.18.0.0/16" - "10.64.64.0/20" # CPS - "172.31.1.0/24" - ]; - }]; - }; - ops-testing = { - ips = [ "10.254.254.2/30" ]; - privateKeyFile = config.sops.secrets.wireguard_key_ops-testing.path; - peers = [{ - publicKey = "+jZETJfwaRiM+7ys5eYjgiWEAtxP47RzZSCx0w4l2nI="; - endpoint = "3.68.138.217:2048"; - persistentKeepalive = 25; - allowedIPs = [ - "10.254.254.0/30" - "10.250.0.0/16" - ]; - }]; - }; - }; - - services.dnsmasq.settings.server = [ - "/vpce-0de71527ea27288f3-9op2d61c-eu-central-1b.s3.eu-central-1.vpce.amazonaws.com/10.170.254.30" - "/vpce-0de71527ea27288f3-9op2d61c.s3.eu-central-1.vpce.amazonaws.com/10.170.254.30" - "/ccs.tradebyte.com/10.170.254.30" - "/corp.ad.zalando.net/10.160.19.100" - "/develop.sys.tradebyte.com/10.0.3.1" - "/instance.tradebyte.com/10.170.254.30" - "/internal.production.core.tradebyte.com/10.158.224.2" - "/internal.development.core.tradebyte.com/10.170.254.30" - "/rds.amazonaws.com/9.9.9.9" - "/tradebyte.com/9.9.9.9" - "/tradebyte.org/9.9.9.9" - ]; - - services.actkbd = { - enable = true; - bindings = [ - { keys = [ 232 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/brightnessctl s -5%"; } - { keys = [ 233 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/brightnessctl s +5%"; } - ]; - }; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? - -} - diff --git a/hosts/hafnium/hardware-configuration.nix b/hosts/hafnium/hardware-configuration.nix deleted file mode 100644 index a9bc65c..0000000 --- a/hosts/hafnium/hardware-configuration.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ modulesPath, ... }: - -{ - imports = [ - "${modulesPath}/installer/scan/not-detected.nix" - ]; - - hardware.cpu.amd.updateMicrocode = true; - - boot = { - initrd.availableKernelModules = [ - "nvme" - "ehci_pci" - "xhci_pci" - "usb_storage" - "sd_mod" - "rtsx_pci_sdmmc" - ]; - kernelModules = [ "kvm-amd" ]; - }; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/b86310f5-fe3d-4b4d-bc02-ab0d7e9297cf"; - fsType = "btrfs"; - options = [ - "discard=async" - "noatime" - "subvol=/nixos" - "compress=zstd:6" - ]; - }; - "/boot" = { - device = "/dev/disk/by-uuid/564E-26B4"; - fsType = "vfat"; - }; - }; - - boot.initrd.luks.devices.cryptroot = { - device = "/dev/disk/by-uuid/d9b120c1-5e80-4893-92fe-497e5b44c25b"; - allowDiscards = true; - }; -} diff --git a/hosts/hafnium/secrets.yaml b/hosts/hafnium/secrets.yaml deleted file mode 100644 index f2bf06a..0000000 --- a/hosts/hafnium/secrets.yaml +++ /dev/null @@ -1,42 +0,0 @@ -wireguard_key_tbcore: ENC[AES256_GCM,data:/VdCVC6xciihm2suOiuNabAWPhWPGSyWSKbLKRpy8EK7aXpyxZPybnANc1E=,iv:/LxrjPLzUkHdyT45RIfbfc4Xa3vsnQNiamnbiMdubpg=,tag:N5nFx1QsH9FGiK9DrMg2hQ==,type:str] -wireguard_key_ops-testing: ENC[AES256_GCM,data:FiADGmh3GAK6LI9Y5EEErmoVCfx4So6mN3glnzUWk8zDXJbRYP1Uj1kJiss=,iv:7tEWVT6eeHpekgkO17DXtrO7meFvYo6xV4ZLpGG20PQ=,tag:Mtr2gMnCqfJP5ADyordddw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1ahnfjspcpwxxk7getcxkj3fypwt37rr6p3xsmp8n2tqqqz8jtg7q2am0et - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtWlZBSFBKNXJ4QmpDZUpT - NE91ek10QkwxSU1XTE81cGxHZXZmL1JncEY0ClZFbVd5dG14L1hqQlRWTDVkZmpx - V1EzSG9rMC80WTNIZExXOXU1VjcrMk0KLS0tIElWdkh4MzNyeTNteDJTY3RvanQx - ai9YdFdleXNNY2pXQzZMem0vdDdSMjgKvngMU5Y1/Pp+G/a9SyewkN9wr22ZcGP6 - XHHadzk6NE7BJWqquY+2B0Rh3B1Ow+rC8yJd7FhJlHw+i0Bp/d/ESw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-04-21T08:09:31Z" - mac: ENC[AES256_GCM,data:+TB7XQPMQCFAR/0jrUKTgjm2yJ7qJ6Jak3DMbFof7mnGE9LKT+xPKYzPwAM+4aDzngHv1fumD6JCXDoJ4DS95frAVfNVNM1bfB0iVmrtf0PX1y+Em189/hs3bt2YBkvvW9kYJMq0g9VBngX6gwGuaBAFHly1gi6SPMZN4vNRF6g=,iv:DK5OYG+BohxllorP0j9mvQ7MtqVNnBjJ3Nf378scJOA=,tag:lBwsHbY9PlJ2/eMtKcxZxA==,type:str] - pgp: - - created_at: "2022-04-20T21:27:25Z" - enc: | - -----BEGIN PGP MESSAGE----- - - hQIMA6jlFWJ+id7kARAArP1hdPwQk2XyKsXYnSj6vxK81GhfZp3tkYEqsU3Jdpwn - OR+0SnuoNWk4dN4JE4ooS5DOhS0ZaVsglLPtiLLohGWYY4OrX33JHZN4oEa5GMBK - t9b0YNb9owow0MSFN679tmiCMvzXGprT0mdWO3/X/HlKvCcTYPRqul4BVeVR/LyG - V94MSaF3BUwFb4p/Q8jcWfsfH5gmMpiFHQsmtci4LjDHvAVCFzI3AjcbRRJUfO5v - ampZ+9yUNo8Y6btrQQWvMoGpOp6U7cj6rTk+eZuW16/7WbHMz6WSpolDyy01QjzQ - szS5RuACnUTMqG4YWQk90H3Srgq/6CFBVLSTm2h8zdO9UZcgkJRYLTFczbYbyqgN - 2Vpjf0UwIv5MHvdo1QZJeBEl8TxjI5UZY2/UDOb9OZXktcAxW5U0Wy6pZIfUsJpk - GJeAb+P3pLvs62hkNSS+rGoGvLX2u0R/Xvw1btTdLLOeIOPNGF8lau32mBuErIZ9 - 2E44N1qV8uQDkDdvaKpj4ikf/0MURPW4GWXST3K/BwD1Gos2SzVD17kXGGOVdeOP - Q19LSo06h2Cq+zNcyKU4C0IdRPvFLKJbyEN3vDYXGnJK7lqGr/UDDcPgYPHVPn1Q - gTdmAk2e8lZY6O0OP5tth5cMjJZj5msvjbww9J1PA3VnBuo8+17zCJ/IYwCUlEbS - XgEWH0LKnwjG7Ufr8eT0DzeCJoD2U/2h+8/+Q2dc4YqokIPW7VuZhR+HZygVAX65 - 1yT/1z+1Hr6kLr9cDLzjyPRu5rNgZJHc8pxkbrQsT764oclvfbgIcmvko9Fsg4o= - =S5XT - -----END PGP MESSAGE----- - fp: FE170812543DF81393EA56BA5042B8317A10617E - unencrypted_suffix: _unencrypted - version: 3.7.2 diff --git a/hosts/iron/configuration.nix b/hosts/iron/configuration.nix index 7f1580a..57a422a 100644 --- a/hosts/iron/configuration.nix +++ b/hosts/iron/configuration.nix @@ -1,24 +1,25 @@ -{ inputs, config, pkgs, lib, ... }: +{ config, pkgs, lib, ... }: let - zfsKernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; - disks = [ - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103837K" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103838A" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104926N" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104934H" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0W206517Y" - ]; + interfaces = import ./interfaces.nix; + disks = { + slot1 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103837K"; + slot2 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103838A"; + slot3 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104926N"; + slot4 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104934H"; + slot5 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0W206517Y"; + }; removableEfi = true; devNodes = "/dev/disk/by-id/"; datasets = { "bpool/nixos/root" = "/boot"; + "rpool/filebitch" = "/filebitch"; "rpool/navidrome" = "/var/lib/private/navidrome"; - "rpool/navidrome/music" = "/var/lib/private/navidrome/music"; + "rpool/navidrome/music" = "/var/lib/navidrome/music"; "rpool/nixos/home" = "/home"; "rpool/nixos/root" = "/"; "rpool/nixos/var/lib" = "/var/lib"; - "rpool/nixos/var/lib/qbittorrent" = "/var/lib/qbittorrent"; - "rpool/nixos/var/lib/qbittorrent/downloads" = "/var/lib/qbittorrent/downloads"; + "rpool/nixos/var/lib/qBittorrent" = "/var/lib/qBittorrent"; + "rpool/nixos/var/lib/qBittorrent/downloads" = "/var/lib/qBittorrent/downloads"; "rpool/nixos/var/log" = "/var/log"; }; partitionScheme = { @@ -27,16 +28,16 @@ let luksDev = "-part3"; biosBoot = "-part4"; }; - efiSystemPartitions = (map (diskName: diskName + partitionScheme.efiBoot) disks); - iptablesAppendIfMissing = rule: "iptables -C " + rule + " || iptables -A " + rule; + efiSystemPartitions = map (diskName: diskName + partitionScheme.efiBoot) (lib.attrValues disks); in with lib; { imports = [ - ../../home-manager/users/jalr.nix + ../../users/jalr ./services + ./ports.nix ]; config = { - system.stateVersion = "22.11"; + system.stateVersion = "25.05"; security.sudo.wheelNeedsPassword = false; @@ -46,35 +47,56 @@ with lib; { useDHCP = false; networkmanager.enable = false; + bridges = { + "${interfaces.lan}" = { + interfaces = [ "enp2s4" "enp3s5" ]; + }; + }; + vlans = { + iot = { + id = 20; + interface = interfaces.lan; + }; + }; interfaces = { - enp2s4.ipv4.addresses = [{ + "${interfaces.lan}".ipv4.addresses = [{ address = "192.168.42.1"; prefixLength = 24; }]; - enp3s5 = { + iot.ipv4.addresses = [{ + address = "10.20.0.1"; + prefixLength = 20; + }]; + "${interfaces.wan}" = { useDHCP = true; }; }; nat = { enable = true; - externalInterface = "enp3s5"; + externalInterface = interfaces.wan; internalInterfaces = [ - "enp2s4" + interfaces.lan + "virbr0" ]; }; firewall = { - extraCommands = lib.concatStringsSep "\n" [ - (iptablesAppendIfMissing "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu") - ]; + allowedTCPPorts = [ 5201 ]; + extraForwardRules = '' + tcp flags syn tcp option maxseg size set rt mtu + ''; + interfaces.virbr0 = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 67 ]; + }; }; }; services.radvd = { enable = true; config = '' - interface enp2s4 { + interface ${interfaces.lan} { AdvSendAdvert on; prefix ::/64 { AdvOnLink on; @@ -89,47 +111,59 @@ with lib; { noipv6rs waitip 6 - interface enp3s5 + interface ${interfaces.wan} ipv6rs ia_na 1 - ia_pd 1/::/64 enp2s4/0/64 + ia_pd 1/::/64 ${interfaces.lan}/0/64 ''; + jalr.luksUsbUnlock = { + enable = true; + devices = builtins.mapAttrs + (_: _: { + keyPath = "iron.key"; + usbDevice = "by-label/RAM_USB"; + waitForDevice = 10; + }) + disks; + }; + boot = { - kernelPackages = zfsKernelPackages; kernel.sysctl = { "net.ipv6.conf.all.forwarding" = 1; }; initrd = { - availableKernelModules = [ "ahci" ]; + availableKernelModules = [ + "ahci" + "ehci_pci" + "sd_mod" + "sdhci_pci" + "usb_storage" + "xhci_pci" + ]; systemd.enable = true; - luks.devices = lib.listToAttrs ( - map - (dev: { - name = "LUKS-${dev}${partitionScheme.luksDev}"; - value = { - device = "${devNodes}${dev}${partitionScheme.luksDev}"; - allowDiscards = true; - }; - }) - disks - ); + luks.devices = builtins.mapAttrs + (_: dev: { + device = "${devNodes}${dev}${partitionScheme.luksDev}"; + allowDiscards = true; + }) + disks; }; supportedFilesystems = [ "zfs" ]; zfs = { - devNodes = devNodes; + inherit devNodes; forceImportRoot = false; }; loader = { efi = { - canTouchEfiVariables = (if removableEfi then false else true); - efiSysMountPoint = ("/boot/efis/" + (head disks) - + partitionScheme.efiBoot); + canTouchEfiVariables = if removableEfi then false else true; + efiSysMountPoint = "/boot/efis/" + (head (lib.attrValues disks)) + + partitionScheme.efiBoot; }; generationsDir.copyKernels = true; grub = { enable = true; - devices = (map (diskName: devNodes + diskName) disks); + devices = map (diskName: devNodes + diskName) (attrValues disks); efiInstallAsRemovable = removableEfi; copyKernels = true; efiSupport = true; @@ -139,11 +173,11 @@ with lib; { terminal_input --append serial terminal_output --append serial ''; - extraInstallCommands = (toString (map + extraInstallCommands = toString (map (diskName: '' ${pkgs.coreutils-full}/bin/cp -r ${config.boot.loader.efi.efiSysMountPoint}/EFI /boot/efis/${diskName}${partitionScheme.efiBoot} '') - (tail disks))); + (tail (attrValues disks))); }; }; kernelParams = [ @@ -152,32 +186,66 @@ with lib; { ]; }; - fileSystems = mkMerge (mapAttrsToList - (dataset: mountpoint: { - "${mountpoint}" = { - device = "${dataset}"; - fsType = "zfs"; - options = [ "X-mount.mkdir" "noatime" ]; - neededForBoot = true; - }; - }) - datasets ++ map - (esp: { - "/boot/efis/${esp}" = { - device = "${devNodes}/${esp}"; - fsType = "vfat"; - options = [ - "x-systemd.idle-timeout=1min" - "x-systemd.automount" - "noauto" - "nofail" - "noatime" - "X-mount.mkdir" - ]; - }; - }) - efiSystemPartitions); + fileSystems = mkMerge + (mapAttrsToList + (dataset: mountpoint: { + "${mountpoint}" = { + device = "${dataset}"; + fsType = "zfs"; + options = [ "X-mount.mkdir" "noatime" ]; + neededForBoot = true; + }; + }) + datasets ++ map + (esp: { + "/boot/efis/${esp}" = { + device = "${devNodes}/${esp}"; + fsType = "vfat"; + options = [ + "x-systemd.idle-timeout=1min" + "x-systemd.automount" + "noauto" + "nofail" + "noatime" + "X-mount.mkdir" + ]; + }; + }) + efiSystemPartitions) // { + "/proc" = { + device = "/proc"; + options = [ "nosuid" "noexec" "nodev" "hidepid=2" ]; + }; + }; - hardware.enableRedistributableFirmware = true; + hardware = { + enableRedistributableFirmware = true; + graphics = { + enable = true; + extraPackages = [ + pkgs.intel-vaapi-driver + ]; + }; + }; + + virtualisation.containers.storage.settings = { + storage = { + driver = "zfs"; + graphroot = "/var/lib/containers/storage"; + runroot = "/run/containers/storage"; + options.zfs.fsname = "rpool/nixos/podman"; + }; + }; + + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; + }; + + services.zfs = { + trim.enable = false; + }; }; } diff --git a/hosts/iron/interfaces.nix b/hosts/iron/interfaces.nix new file mode 100644 index 0000000..dec89ae --- /dev/null +++ b/hosts/iron/interfaces.nix @@ -0,0 +1,4 @@ +{ + lan = "br0"; + wan = "enp0s25"; +} diff --git a/hosts/iron/luks-passfile.gpg b/hosts/iron/luks-passfile.gpg new file mode 100644 index 0000000..96c9903 Binary files /dev/null and b/hosts/iron/luks-passfile.gpg differ diff --git a/hosts/iron/ports.nix b/hosts/iron/ports.nix new file mode 100644 index 0000000..7a60b1f --- /dev/null +++ b/hosts/iron/ports.nix @@ -0,0 +1,35 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + calibre-server.tcp = 8081; + calibre-web.tcp = 8082; + esphome.tcp = 6052; + grafana.tcp = 3001; + home-assistant.tcp = 8123; + jellyfin.tcp = 8096; + matrix-synapse.tcp = 8008; + mautrix-signal.tcp = 29319; + mautrix-whatsapp.tcp = 29318; + mqtt.tcp = 1883; + navidrome.tcp = 4533; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + photoprism.tcp = 2342; + postfix-relay.tcp = 25; + postfix-submission.tcp = 465; + prometheus-vodafone-station-exporter.tcp = 9420; + qbittorrent-torrent.tcp = 59832; + qbittorrent-webui.tcp = 8099; + radicale.tcp = 5232; + rmfakecloud.tcp = 3000; + snapserver.tcp = 1704; + snapserverHttp.tcp = 1780; + snapserverTcp.tcp = 1705; + tvproxy.tcp = 64321; + unifi-http.tcp = 8080; + unifi-https.tcp = 8443; + wireguard-esphome.udp = 51001; + wireguard-public-ip-tunnel.udp = 51000; + }; +} diff --git a/hosts/iron/secrets.yaml b/hosts/iron/secrets.yaml index ebac9c7..3a51f2f 100644 --- a/hosts/iron/secrets.yaml +++ b/hosts/iron/secrets.yaml @@ -1,41 +1,54 @@ duckdns-secret: ENC[AES256_GCM,data:SAf/xZ28tgmvqcVKC2tMNRm838AVMMNCC3fpYLXBEIoTl7E7,iv:+KTEpNMj0+aVCGKB1dRFFslgjpBhSzBZFdee+VIAt4o=,tag:C/eSyoQjAgD7Qv4J4jsp4g==,type:str] +calibre-htpasswd: ENC[AES256_GCM,data:+WW5A3/GZIk0p5CJ8RnK/gcYpJIXXsfrCpqFUWz2PzFZwf5xOlUeTGxqZdNorKq5xip4sT3/brrG4mqGDJ7iXfXJ,iv:D7CqUlbX4XGuUhjRLKytgvLa+jF4zuTZGV1NZCehf8Y=,tag:TAjSyBCIMZC4LBm0Q/2aXQ==,type:str] +radicale-htpasswd: ENC[AES256_GCM,data:Q0WnleP9I4xozsL/H+5oV3Ag7khfalV40A6ub+DA07U8UKna3/ju533RmjWOnETzSNa6XK140nfCcfGZCiqGyF9tfuuXcKFu+j4=,iv:87PSvHyKF7QUQZmEuxM+IT0VKSGnS0MjoUmCqJ+6tzI=,tag:yrP3TgxE8aSZf0MrCF9dsQ==,type:str] sturzbach-htpasswd: ENC[AES256_GCM,data:qqBwu6mASnRqjy65knU4uIvBNXXgrfcmvWnbmOH4tVQ7vRbpEhe/GQDwAg==,iv:OQnDOzezjajGl35m/u5StQeMRR+1sNDD5u1my1wTngQ=,tag:7zjVRWI1IzZ5iS3sFHLubg==,type:str] navidrome-password-encryption-key: ENC[AES256_GCM,data:ynQsFyGDEBnlWhTlv0mF7mLiXOjijq9ixWWEa1OXsTOYAd74dU0dp3Fo532WtD4fPvIWEf8Y2dYmY7zPVLuydQ==,iv:GJqPVL5OIFPLMcCVOjWvMjyFR4iTXo3uGE8R0keTzG0=,tag:RTERQgYRxBBevlL2H1lIWA==,type:str] wireguard_key_hetzner-ha: ENC[AES256_GCM,data:ak/KpQIHBNRPriJ1IeKYXIp4CcnygRHSj5MzZNnuxQnVunmmtzGu0lBEajA=,iv:aNw3EooT6XE1zC+g37WSJasRCfnNUaKQrYCDBMTxRrg=,tag:KXc70tVFc7xDLlefk1Hzow==,type:str] -hetzner-api-key: ENC[AES256_GCM,data:7eWYncujkEytQzhRdNRItPgpz1eUvcyp2PVLJtHbqd8=,iv:AxoKJUuor32kC3ZdpkDPUEUlPRosY6cKoWx0TIGK9wA=,tag:SVtXMraGxnJnx/j3zMQnQw==,type:str] +wireguard_key: + esphome: ENC[AES256_GCM,data:sMA/a0YsS/9ReJDY6gpIw+nTjkMyhs3GyEy6nA3Fiw2mvBdZCyNg0q8tdy4=,iv:WPVk4BlY7eKTjLuT/Li0oRhA9N16WFBnuuGKFjHIhLQ=,tag:0x3+3+ts2zMg2Q4eqySNnA==,type:str] rspamd-worker-controller: ENC[AES256_GCM,data:7tS8bEr9i5F+YZoj3uPQa6Xd2SCsuC+jE531AbKEmPHNeL3qMyO0pQZ/P1ONaPHTVMOPQHYABihDJcZv0BKW,iv:pFBVi4F661fnYPcCPwuetiGL1H+RAnJiFQhTUqGNwjU=,tag:xQoHIEQpnrMOnXqsH8anxQ==,type:str] dkim-keys: jalr.de.default: ENC[AES256_GCM,data:mnApsYKXYGtUAHddccmNmU9yZQtekDkTiTXbJ0UJxC0rFxzQCtGsinQslIROJdNUxsxciR1ilNzxawzjJD7AaWJbcAq2TYObGJJOQZBif7t/XEN/rIxEmnAFmdeAyrSONmFb9DiEn59m6DpsU+/9Y+hnc/uwwbzueO34WHJnTqmmsxFVNQZfGR+cbSckHS3wZrfjZSKKzCRt+9DU/xxJ4voyowXLO77w00LHVkyU5liwONi0v2XJ+QeP/jIMmJeKjujZcH+qvUm/kukijqyWKGrZoAYPC2cBlL/UrNECuVdSLMXvr4KBDDTCRZCSMRgUPJ0TAfpQPTPitKJ/0igK7qQl9n/6hckY7VyP8KDS7J7G2Z2XVxfZrAR4X/7ya9B2kneVr2CNx3w954EdTcV1/lD7rcKRjKynyl3ddf8gxJFJ21k1ybo2RLnftGCRVq25qNwhyfjU8x5c7AEs+YTPDrcnmxZ/Ui276eLwpMj61oZzTp8QQhiBVwS/+ruRLC+78pu2gb1gBF/Oo3nuvQD1SOpCRikLVewCYDvfXj/hrjo+oCsjTOj+9tWRcRAEDVlhkXWCMuPXDYrdt3HrIWbQuP8NW1ezd1Ll0r1ujjtPJeSwdd8cVcUSBIoA5gU+eXnYjFaSx9BZ+sIfKqG//W3S+aBYDqAEK/z4N5q66sReb5mtSQYfbZuIZDmox9bwNMG3tJmQX0lJZgEIiuJ5/ef4ra0sj9JsRFldmIn9KUmjW9OlIwzQ42cNNvQSMD/6haNiYsE6TPzVylJ/B2kNu9Qh5FfpCIPtVORv2BAGoNvZlyhjyEiXBEZ4x2hx1l5cBwGOaGhoJ0p+1wqn2zDalIBaEFjbBVdIB6DPC6/lccvpqSwF7HvW2ugyYhW+u92vgic71/BsI4i0OlsJV18gU/zVg0Yj8SK69kEwm4wkJTrkM/I4+kkUIc5OiSAknRfjOFJc0etkh3nO34xpHLOkSv9DrKfXSAGmGZtCLtVL5LGdZeCd/g6EK0JJh6bd9Gu9koSJVq5vjdDJJFf+sgk39TCvHAvk8k1/FgdK5jMJ+pR8heJtP8G96ay3DFVm5hpbjuNKqfBvbf2rkyV6++ywRFnAQGPUiMn9g6Q4F5Ks7CC1D0Ubl7b3dCUk6BDi8rHjxy9QS0/25Yz9cF0bFd6XQDfblnyRLMi9aB36M9Vp38Oh5aB16MyvNUHzcxpaAak0yknE6OuuEMBPQZgFVADCITfy9eUXl2FoXrMWEnBO78GybQ+cV8nhynn5t0U+3koMy2E8ju5kiEofQxXylys3Q76iKRRUbQqFkh/ndWtJVVfGNpi1GrUr1w1YZM0hBY9FqqeBjf7ckj+9BdiwWJ0XauuR70o7odm02mydk1/T3Hfzt3OE5nHIXnVbum9KyPx8wXj9qc6JGFm558pQOcRUgGUi+EzGoGckkoLx4Onl+XeGysW5sXP9dbYgMBug0Tjmdo9xkoBti6znDnN/zh93bbzWITNvxMgVs8zSWEhlM0c7F02UeUXSekbTFue5FOaMdYObMvPeb53jAKBOYLr34GVFvucJhKajIaNzDvfiI6fGCMxcSsWk+P3co7gdbRlWYZELsKDu2scktZsHr/gRwRiDZXAWOLiWZL4jswQ1vXSFXJgdblEV//hr2DwsAtCAsyFcgO/LGq30xi3xNqHTkUZXo6cZYSb6EVaIywMCI5ySEnTLAp/xedySANHuo8yyVqyLxkDPI7CnnSS7JcnQF3K5z+NZ0KnIpc1ewGupOhS0fKj31XxUkoSsHEY/iWJPLNA8+4VsBkADnGdkYXHTvy/yAGV6w1k1qtjiWhDAGcE9/o6NOHctYm3cx8CVsLpve/WFUaCkGgjWJdC8XP92xsUQoE6PENn6ZzFaqGHs7hgQqE1kBcEj8N5WkEqkoMo82giHE33iYoVUdkjOTkV4iDGEqyjg1BoM0GedR2A832LseDkP7u4DjIAQfpIDu7PaeiDh7xWkPRwIMV0oDTakXTdPkPGdgFikzTaxkTzRlpCbQuV769eITqVT04kJDp7+0Rb6dtjeXc0Ennv68wZSiyrlmXbrJntg7g1wrebq28q9NMIZETAPugfK6wNDu/Iw1q1kZn2ELo6xaDlcIxHDcpzK7e2VAYYuP1k3sYnSLU3oeq54j3/yS2z1me5FEqWlPOCrjdnLkE3/GjbeMsYo2YTYJEUEd2ncacSCoXUaUoxpBnjRYcHLRUV+6jy7Amp0/52rAPzSeVlBzc+SdNiKLYA2UQ74WrMU596Gkhw1SD8jSM5QqSBhH9sL+oE4GjhjLhstMUPdkNgiwxXDTZLKcIyjN1cn+RSmvNA2KXMH6MoXrkqSkJ9u2s0QAhla51zR/LZwWbzwGOO0dkh3rwh2x+pcCfuzvlk3lYr/x5XOF2k1n8yvehXY5zIX8nk6djjLbvAzzSr/yalS7R0WYIc6CjzoUl3qz+PlneMfKHcaX00hkOlIub/ZFQf1RE+JzZxi0qQq4M8Nt1XRKGDeS448Z6znDpedStUH29krZcnjMtyLmPX7ETTsjr3HLpCOd7MQ2K1rfhmvh5BtJkn1KSUf94puZbkLH7X+WnWN0hsc+KbSXnYZvqwJ8G0/7ptp/Q+wGljqhjv+HhOeA3NUwANv1xWgbiymVIlxCodXtQwn8mxS+jxSvslGwOnyUkTT76IbFbv/IpW6PNvj/xqwOqey8a/4WCGcqs403Y7TKQ+xCflG6K3tL7U5UbMnMgXTeZvoK+DooS2eIepF2WB5XqTuOZJV2OQ6GHfaBMjXN9iGVNLi6XgkbpmcMLQ4TZq+dVmgleJb14IaTFD3n74OfmbcT9lmRfPRJEpFEMNeL3ghH54P2a91zJFASgE7x+Uv2cGcmKFtMbyc/rrhH1F/Ixlv/R37huFo1T2dPMEZ/1ouuPpbUQ5oz/JlOWw3NOxd0O6oG0x9Xib+9KxSFOusLWcFEgx70jrBQKj8s2Jj+W0gZYv+BJtPMPY0KAkRj1amt4Fd6ZrPOEXJ392EHSAEv5jssO5ba52OHKA+QkYvPPL04rwkxSAQiTl57scnEj2WEIP+Lz0/qsMnwF+3rWuz856doJZcXX+U9iuzBCaYQqA1P3BojAYhEHnXBPeolHOA3BmhT9E2TJsZ6P9SQ+GaqyLm0i4vRXGlArlkLwRBs9EZv/l4DT8q0YHha53O4rhRzGJZKAOO252Dpha1YN7+FubYGAZjaUT5O0R/7xSPrGyBejddtM8asW8+NClAn4Y6xvj1IgUg6VRpEy7ZIpZEQ+UyDWt0A4nsipaz2NyZKZ5Vxza2v1qZDdYODK8nm/zj7fR/JykaNVEVj7ceTSHdaQlajfeEWWTs92msIBcqPUXqlaR005hoVvXm+WCnzIMIXLGiyRKRsAPIDYh2hGCtvfXLSq5TYm3bnGAImL0KW3Yllt1qSqSbOYsvm5QfDmTrrccvtSLGRj0rOU3Z8f4WXjf+1YgxjZ9h8fKL+LKA8x1S6M8fl0JVGBIAU8Xe8c4+r2F1VcygJp7h+0v8o8GudM6in4djAdeMLWBgXid7r0q744joFucP56opwYQp3Lu0oFEo0omS6Rh9yPfOjdGBU2eUdjcCNXXuEJD9yHSyebviSAvDw/KH1AxYSWYnjMWACCfcbOlXf3ej7PuQgq5MdFwF7+QawXm0john4YusUon4/0fqd/IFLd6oHYYesxcFdm1jN6DeS4SAqRgeEPuEWDFERgXjLHBxl5Xdi5n+NOR3Vc7ziJ9j9/CA1DKdwmsFBBDcVKMnr2FibXpN5WsSdlBng0L2zhkL22wRH9xbz8Xk5shN20/EHoxHB5HJvwfOgHIC7ooWKOUUuNTZH43+gVN+wzRzlMfiF4X71Edw+lTnQRp6Lh03M2k9do6JPoX2+UU0h6mOYiAFkhHKzCmK3DY12c4Smx+qLJNbUGhoMgthu/WnXObm0Hr+myCooTYSVNTJx6vVjI3GZtMcat2o8B9k38u/Y5/FxqTYmyXhROwS4v3W5fXwTAaxBqQy6Xj5s4V37omBBh/Z9a43nc2VlT7dKR1wIvNB/gqhiYyYrVMtYMJqGLkeCbu50LUWT4qXyR8uaqbZTVjyJCQRxZd6fd3Zfe9wIeYe3N5qKIXkFD3n1U2Q/EyRfb3TpiA+eYkAtl6JGK0vpeWpN5M2LJ3/V79e3cIG7B7/p6BrRxKxHDnBZcu57KKaN8XM+v2KTz7XdF8bjgeu1V/B9WoBwnpzCM+3s5ffNceuUcb2gJgRAUpZvcSDLYy+9aluGU2Tvsm49fCzr851p3VSEJepgPpnvuq874AX/MbPvqidF8Y21Kss1RUbl5wrlq5IihKdM+xCSq6mjvtSPVHRvw==,iv:2NBiTTW9slOH9BvM+kVbMB/+8EiS/Dc/eaqrtiwn4HY=,tag:0rc2+ZWy9XZYE7RK/oSo3g==,type:str] -radicale-htpasswd: ENC[AES256_GCM,data:Q0WnleP9I4xozsL/H+5oV3Ag7khfalV40A6ub+DA07U8UKna3/ju533RmjWOnETzSNa6XK140nfCcfGZCiqGyF9tfuuXcKFu+j4=,iv:87PSvHyKF7QUQZmEuxM+IT0VKSGnS0MjoUmCqJ+6tzI=,tag:yrP3TgxE8aSZf0MrCF9dsQ==,type:str] synapse-turn-shared-secret: ENC[AES256_GCM,data:Q1XRds3Zud1kYkvD6s9WUzP+kNDNsxB5SHd6oCAaLCHhHhYENSAYTZOF+rGjCPNyKFL0e/A=,iv:zScRQrz+pXHNUh/BGOaV+TVnDR3wu1Z/UO1zXarKwtA=,tag:ckpVziE+yb0FjctcT7tAkg==,type:str] +rmfakecloud: ENC[AES256_GCM,data:ktKBKb6cRv1VF8tRvXIpxIy9hPinVPKK05mgvYzz18PEdcrCLpldm5xf7ffHtY5XzDOAMXDCiz6x4xyv7071frrF0spOEPnIzVhxwG8H2Ck=,iv:qJdHjv0RziAs4G9UGeRwGQ4GE5kaObJWpIYWpRKhr9c=,tag:PXgvU1hZK/gvWGyFJaHekg==,type:str] +esphome: ENC[AES256_GCM,data:SPZ/4GqNuz3Lk0Jor815jWaw9YYiIS/u79qmdjd9eNhPUiT4PpM7gQJHLwa/KHbjYAHBICVd5dAFdBsZ5UsYww6bTZcEjM+aDcSXvrXE7NKUdYwrKfOeGdsIX/l79AlNfaPma1+3dDULj6/5ElXZJFsuurMWaWc+KLOUTBTMNRwylW1pxShywz40wqxtTQmFBLhIbk/yYLfJMuFaPnctoJnW6cutbzw/fwSitAvYEq9Ch2ZOkujOOTP/NlDAsxxBV6tFJj7UnJtGJ9lucJ7BURU0eb3Bp3K3ef1XgiyTtp0g4m2EdN/XTzzA6fI4/Vhf6giDvrbXxSM5pPTbY4fjv5cpKROaHS58il24OA0S7zccFY8XoXkl2QjiJrNnsJvNrfxn21i+LFWKCC1CMZbfEMkg+FNjzrp6nkr/kpjOlEet7umhlkxXdEUSjUBmb7luig55ICSxCH5yzRD8TwsP4fhjQgAQ8C+5fojQ/YH3VkFboY4sS5mkamDeUJ94pLy3GcfKPB5I/qipi5kUxEo7AvvxK2PHNM/0VC3S/23StQwkrH2+mrU5mo4+ngutoxyegSaqJzMJZ6KORPbDo9ETxehwWGZTuP+kpjT8Sz3J0A1LxCZwQsbXJMpABWkg2c3URJZYS/1q1o+16PocE9mbPthiayrBETss6zsYB7xYjaRMN74C1yuEL/55xRqqlrNgfk/svW4XTNRl+zkRfyxz1awWEa4zSpbqSTZT5M97uxuSHwWW64dOb7lCxfVZtpBuEMTN2iITUYWfNnyv3Ipgv2AN3vZUoYiehS8Vlv+j0SHrTAjLqUv/opTNB02PMpE9wWKsOiktDQ6VwbPkd/k9xCPqdaT4fN/yinji+91BkgeaNgyu3KRTnaQlFOxmYzQAoPHwhZSwT+s0/rOPP4wtKIUkI2IcMjEWd8nw0PEo9J/wqzZxcFZ8VKSJofrVMmo0nQF4BK0/86KXOTk2bo/5HaXE4d2s6xc3qhyGnWdWUjm1X8CLoVNx49EsVRhrJKkqrvgFH0P26qNno4m7KWtzZGt/e/e3B4+TeaVgFS5o7IY8hcnIslsbic9DW/rSAxkXdumgvumRHHtBf0WA89K84/Qhs0Ufx4z9ijZdojqTX4rRPpIpV79jaJmjS72adUjnVWMMdQWyW7Y7e0Cp0u7dcbhl1T2S8QRMsJiIOv8syiUFEy8krC7R8Lf7/kZca1MYohEud/QeNsIWq0FfpyYN/vGMoTZ0Hn5npuXap5HVlmSoPX8Bve+tgR3ZFBpTS+/uXhJ70v93IXBt0fWe5tNl518Di8k5h6xAp/8Rl9EJdUuKjVnpto8bO8LIefOQLpeTo282ykPlqxsaEimeasExNAMyi/dzX9SKuSr9D5zKzU8/0zhUA+lhKtwkUkBuxeaeqF1mlffWyjVhL/3/il9Xd7kTT461889AOKt20BH1rzqVQcUiLgoi79r4kIDs2MJE8QU+Ne3fcaP+DCvjjfiTGQ+0icZvVmgKYRpJaD7MsQWN0LvzoqbkIlXrSJprsDt9JNX4IOp6egh5bxAMwZM2V7uCF/WRDXSlHYa6XJId6fY7PMAhDsTYuD4/glFzeD121yajoeyb71AgoKy62mvgExe6nnzh6qNEAQEX8bl+L6sVtwzdYpvc3j0PXbtbEs7zwncyhphj0EOag0N3E236Ck3fW6EWIKkqg7qTBDdXrCKgv7BKl6aYIDQiaUP2hwlqpegklTRkfOrAP0tOYps9DCKGa+Bm6fK2ekG/b8OCPQdp72PtyNv5vumzaV2L6v+nc/0+5VsjL66CdJ9tvwIR9NxUC6M7pp3gMQMyxW95rj4JxZkW8f8r6EU33rQJtNeeo45Uorp3l466s//GoEmcRNO08VcFJaEdd8rocm9dsexd+0QNN9xQVDOwF+KgJAUJi+Qygvzcy+nVibow2sRUPGai6/rbNBPrVEKjfHck+s794XjVGXPAoAMk7SsNKrjSpvEHws0dnC3Faa3PA8UKZalOzmNi6A1ngK/wn5SAQ/LfXRDY/oTJa4ND/C8vJfzywR8=,iv:3nnHepX48XKuKQzO4zHcGj0VNQR2edmQ2DqvAJi5W6M=,tag:UJHGnWXrwCUgyRMGc16Ntg==,type:str] +home-assistant: ENC[AES256_GCM,data:wcFMxDdRCHf/shO9v2WaGgrsa9J2WP62xFs=,iv:9ckeIO62cFZUo8fPyQj445CrJVTooNlwLapM/oTsrkk=,tag:mlfxtXDPsB3T79P9BX9oJQ==,type:str] +mqtt-users: + home-assistant: ENC[AES256_GCM,data:oIjCw7ZnA5iOBmQdW1jcy3QQnpjT32pY,iv:5HFRkXJBdMXQbjk2ubQs3sEy5qEteiqSe2hrNc8+H40=,tag:7B6yI4oCHanE0JE/gHaKnQ==,type:str] + valetudo: ENC[AES256_GCM,data:+HRz6X+A5dhmx43G99ka0u9VozuzOFWR,iv:SPw5yoiBqN7sBH5EofevacTtu45jmuTPqToKrar0aJ0=,tag:lf+usB/eNNP1yuWW/QyTqQ==,type:str] +photoprism: + oidc-secret: ENC[AES256_GCM,data:XTAiUiGZJfSZHNbz6fePl3iMDdbxFSE7+SQH2ECRFqlo7w8TAhLyNXBxlEfGvu+8vttbKdkEm0r7132Q4ftOtA==,iv:WGsQXolbtRWIq4EDgODWNmkXdOZCsA9A3Fqoo4lJyec=,tag:5zJftwB5If/RZB3hI0Ly8A==,type:str] +prometheus: + exporters: + vodafone-station: ENC[AES256_GCM,data:eaFqYEuK3UU=,iv:BauymCkvj33TmZLyii367uVEc4Iq4GGcik4nbyT9Fpk=,tag:poB+qh5tAdv/dEt3WN6yVw==,type:str] + unpoller: ENC[AES256_GCM,data:WI1oUKHW4ef4pBk+mGM=,iv:C1LykPf1/ypUmy3ZCQzjfSjkpxhUukDNnfJnZLp2CJg=,tag:mSnZJKl9IHcx7I7GpFherw==,type:str] +tvproxy: ENC[AES256_GCM,data:MbXEmgerpUiwDgcUKF2y1+Cc+d43sKPfGGTEkvNoZFFS4rzDWw4Udg==,iv:ZDsfSb3HK008e7/J/61iqVRafIzKbtPEdhH7ixo9lSY=,tag:3JbJ+2DJKQ9G2ui6VuWbOw==,type:str] +grafana: + secret-key: ENC[AES256_GCM,data:RX0ox0r3Jwm9DMIfBnsL7ydarlrYSVBjbVXbooHR1Ms=,iv:l8Aud8VyGtz3dNARh6s8/Y6MBtc4xj1Wu/LLJv1e+KA=,tag:+7TFyRPhBS1Tvn2JLBEeAQ==,type:str] sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] age: - recipient: age1hx7fdu4mcha7kkxe7yevtvs6xgzgaafgenm3drhvr609wlj94sgqm497je enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsdnRlZktJNHQwSWdlb2l6 - aFNRS3U0UHM5UFVkTUtqMHU1Y093ZjdNMUhrCmZVMlBNSTlwYjlQaklzZENnR013 - UXFNaWp3WXhQOC81dGFFQXNwVHVYajAKLS0tIGh5ek95NnNRbWFsVkRncFJ3VUdE - TjdZRldhSzVtMkVoTzY1NjdGbCswRVUK0pi+8UuLqRmytcR2ikxOAM02iccl8P1y - ixv0PKPLd+vQ23QeeQy/TfoGx16XttaDUnUrPLZR3TUKtAcld8+m6w== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSnhmN0RTMDcxR3hWMWFq + TjZNNHEzM214Tm9hK2RENm9hSlRFUy9aNW5FCm1paEx2TEEzcEd6cVIzaDk1Sk92 + NXNORGlONHQ1Uk9ocGlScXFIWmlwUDAKLS0tIDhTeDQ1KzhreEpMVFVvbFdiRjVR + SU1USkxFUUY2NVhmUHBhZkdrNDR1Q0kKiXIicInELRjDR3tuyA+lnXeCcd9lYvbV + GnBRGPM7BNO/6AA7HhAei48Kt+XE6+jQX66yTXyviKhK7Lpjrlb2YQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-07-10T19:12:08Z" - mac: ENC[AES256_GCM,data:69VwkQHqDho4JMTyqRQSjSFdgKNdo0Vut9xp63FmPi1lD2EuKi78Mzt7tsGnRoilG8CS8LW+FSaSB/ywNJYK4bmkYMB2N0XbgAs3gAf4bzqDsEfR/WRRnhzO5eM7x4dE4hkknzv4R39e2ENzkWzpR5EBf7UUJUGZv9UcXSHGiRo=,iv:vRWo0J0BwTVJCriT0PZyNMTXlOTXnLBLAF0VJnADqcI=,tag:P3C6JaZahUsPG+FqnHmmQg==,type:str] + lastmodified: "2025-11-04T17:52:25Z" + mac: ENC[AES256_GCM,data:/q98uwoYJsPRLlWxxDn7gJQ0jRxlAfVxEmUw8ayP8gIkWzGN1DCR0jx2LFlSlWEuaPScThw5IhGxbBlBxX2wV952MC7tEoHAAMvMJberG1a6do8zSvotDHocdXVlyj4jJZhQvjUVAmeVsYBY3oRwOHdzis0JO5IW0hxgs3x+xoQ=,iv:9BR0ws9ZzukjxLpPjvl73B3RmLA+c9e7F3AVk5l0SGc=,tag:OJ/iGy/Umlj/82EtZxjLSg==,type:str] pgp: - - created_at: "2023-05-02T19:30:42Z" - enc: | + - created_at: "2024-01-31T01:20:30Z" + enc: |- -----BEGIN PGP MESSAGE----- - hF4D3ylLYNOsO+0SAQdA16evFPF5J4wB4iw3y6rQbjpyVKiU/M7qZmdsKOBpLQgw - CccmnhDpRDvQ2pTlHh674o0flfXTvFQ2H2a7KuVDLerdUuw+aBGD1RB+Ob0Vvfoi - 0l4BKpYchtdJQpQuL2Gy5LToty9EZUVVRvhyIfSasWCdDH9ajNWHFcKn6MX9wj46 - ly6CeFgZKAyyeQ6qWQnft7inEQk7krl53NBrbzDN3Rfz71zmpO97h/av7y7ilffa - =DpnU + hF4DY/xpNY5WhB0SAQdAVIYE8wlQqo3HcaT37fSDHQ5i0CxUv9kyPT9BiDgwxA4w + hJlT2XPeSK6Ob1P++oGUrVhkZCuFEnV+6ZtNjwIu9EfU6azyPZEcmffmi3PdlSvW + 0l4BHCRW14iBbixIlZxloBUEEMCg5n5HNQ5vB4jRyq4af0uYxOgE3doZbb5gaVe2 + sODYeeb0u1DdJRlvfyPgqPvit4tkovzLKsO/RUpHqTuh8cXHA8ibLIXlIUbS7FoN + =aGjt -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.11.0 diff --git a/hosts/iron/secrets/mail-users.nix b/hosts/iron/secrets/mail-users.nix index 4caac02..bc181b7 100644 Binary files a/hosts/iron/secrets/mail-users.nix and b/hosts/iron/secrets/mail-users.nix differ diff --git a/hosts/iron/services/avahi.nix b/hosts/iron/services/avahi.nix new file mode 100644 index 0000000..f15827b --- /dev/null +++ b/hosts/iron/services/avahi.nix @@ -0,0 +1,20 @@ +let + interfaces = import ../interfaces.nix; +in +{ + services.avahi = { + enable = true; + allowInterfaces = [ interfaces.lan ]; + openFirewall = false; + publish = { + domain = true; + enable = true; + userServices = true; + workstation = true; + }; + }; + + networking.firewall.interfaces."${interfaces.lan}".allowedUDPPorts = [ + 5353 + ]; +} diff --git a/hosts/iron/services/calibre.nix b/hosts/iron/services/calibre.nix new file mode 100644 index 0000000..816da0b --- /dev/null +++ b/hosts/iron/services/calibre.nix @@ -0,0 +1,94 @@ +{ lib, config, ... }: +let + inherit (config.networking) ports; +in +{ + sops.secrets.calibre-htpasswd.owner = "nginx"; + + services = { + calibre-server = { + enable = true; + port = ports.calibre-server.tcp; + host = "127.0.0.1"; + }; + + calibre-web = { + enable = true; + inherit (config.services.calibre-server) user; + inherit (config.services.calibre-server) group; + listen = { + ip = "127.0.0.1"; + port = ports.calibre-web.tcp; + }; + options = { + enableBookUploading = true; + reverseProxyAuth = { + enable = true; + header = "X-Remote-User"; + }; + }; + }; + }; + + systemd.services.calibre-web = { + serviceConfig = { + BindPaths = [ + "/var/lib/calibre-web" + "/var/lib/calibre-server" + ]; + BindReadOnlyPaths = [ + "/nix/store" + ]; + CapabilityBoundingSet = ""; + IPAddressAllow = "localhost"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + ReadWritePaths = ""; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/calibre-web"; + RuntimeDirectory = "calibre-web"; + StateDirectory = "calibre-web"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + WorkingDirectory = "/var/lib/calibre-web"; + }; + }; + + + services.nginx.virtualHosts."books.jalr.de" = { + enableACME = true; + forceSSL = true; + kTLS = true; + basicAuthFile = config.sops.secrets.calibre-htpasswd.path; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.calibre-web.tcp}/"; + recommendedProxySettings = true; + extraConfig = '' + client_max_body_size 200M; + proxy_set_header X-Remote-User $remote_user; + ''; + }; + }; +} diff --git a/hosts/iron/services/default.nix b/hosts/iron/services/default.nix index 8f7c4d5..29cd267 100644 --- a/hosts/iron/services/default.nix +++ b/hosts/iron/services/default.nix @@ -1,15 +1,29 @@ { imports = [ + ./avahi.nix + ./calibre.nix ./dnsmasq.nix ./dyndns.nix - ./jellyfin.nix + ./esphome + ./grafana.nix + ./home-assistant.nix + ./jellyfin ./mail.nix - ./matrix + ./matrix.nix ./navidrome.nix ./nginx.nix + ./ntp.nix + ./photoprism.nix + ./prometheus.nix ./public-ip-tunnel.nix ./radicale.nix + ./remarkable.nix + ./snapcast ./sturzbach.nix - ./unifi-controller.nix + ./tts.nix + ./tvproxy.nix + ./unifi-controller + ./whatsapp.nix + ./wireguard-esphome.nix ]; } diff --git a/hosts/iron/services/dnsmasq.nix b/hosts/iron/services/dnsmasq.nix index e96db17..72b5f61 100644 --- a/hosts/iron/services/dnsmasq.nix +++ b/hosts/iron/services/dnsmasq.nix @@ -1,33 +1,54 @@ -{ pkgs, ... }: +{ lib, pkgs, ... }: let - stateDir = "/var/lib/dnsmasq"; + interfaces = import ../interfaces.nix; in { services.dnsmasq = { enable = true; settings = { - listen-address = "192.168.42.1"; + bind-interfaces = true; + listen-address = [ + "192.168.42.1" + "10.20.0.1" + ]; interface = "lo"; expand-hosts = true; - domain = "lan.bw.jalr.de"; - dhcp-range = "192.168.42.20,192.168.42.254,4h"; + domain = [ + "lan.bw.jalr.de,192.168.42.0/24" + "iot.bw.jalr.de,10.20.0.0/22" + ]; + dhcp-range = [ + "192.168.42.20,192.168.42.254,4h" + "10.20.1.1,10.20.3.254,12h" + ]; cache-size = 10000; dns-forward-max = 1000; no-hosts = true; addn-hosts = "${pkgs.writeText "hosts.dnsmasq" '' 192.168.42.1 aluminium unifi + 10.20.0.10 kuechentisch.iot.bw.jalr.de + 10.20.0.11 led-panel-schreibtisch.iot.bw.jalr.de ''}"; server = [ "142.250.185.78" # dns.as250.net "2001:470:20::2" # ordns.he.net "74.82.42.42" # ordns.he.net ]; + dhcp-option = [ + "option:ntp-server,192.168.42.1" + ]; }; }; - networking.firewall = { - allowedUDPPorts = [ 53 67 ]; - allowedTCPPorts = [ 53 ]; - }; + networking.firewall.interfaces = lib.attrsets.genAttrs [ + interfaces.lan + "iot" + ] + ( + _: { + allowedUDPPorts = [ 53 67 ]; + allowedTCPPorts = [ 53 ]; + } + ); } diff --git a/hosts/iron/services/dyndns.nix b/hosts/iron/services/dyndns.nix index 9b4aebf..e53e235 100644 --- a/hosts/iron/services/dyndns.nix +++ b/hosts/iron/services/dyndns.nix @@ -1,17 +1,56 @@ -{ config, ... }: +{ config, lib, pkgs, ... }: +let + interfaces = import ../interfaces.nix; + + mkService = config: + lib.mapAttrs' + (name: cfg: lib.nameValuePair "godns-${name}" ( + let + config = cfg.settings // { + login_token_file = "$CREDENTIALS_DIRECTORY/login_token"; + }; + configFile = (pkgs.formats.yaml { }).generate "config.yaml" config; + in + { + description = "GoDNS service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${lib.getExe pkgs.godns} -c ${configFile}"; + LoadCredential = "login_token:${cfg.tokenPath}"; + Restart = "always"; + RestartSec = "2s"; + }; + } + )) + config; +in { - sops.secrets.duckdns-secret = { - sopsFile = ../secrets.yaml; - }; - services.ddclient = { - enable = true; - interval = "1min"; - protocol = "duckdns"; - server = "www.duckdns.org"; - username = "nouser"; - passwordFile = config.sops.secrets.duckdns-secret.path; - domains = [ "jalr-bw" ]; - use = "if, if=enp3s5"; - #usev6=ifv6, ifv6=enp3s4 + systemd.services = mkService { + ip4 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-bw" ]; }]; + resolver = "8.8.8.8"; + ip_interface = interfaces.wan; + ip_urls = [ "" ]; + ip_type = "IPv4"; + interval = 60; + }; + }; + ip6 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-bw" ]; }]; + resolver = "2001:4860:4860::8888"; + ip_interface = interfaces.lan; + ip_urls = [ "" ]; + ip_type = "IPv6"; + interval = 60; + }; + }; }; } diff --git a/hosts/iron/services/esphome/default.nix b/hosts/iron/services/esphome/default.nix new file mode 100644 index 0000000..4e63710 --- /dev/null +++ b/hosts/iron/services/esphome/default.nix @@ -0,0 +1,25 @@ +{ pkgs +, config +, ... +}: +let + inherit (config.networking) ports; +in +{ + sops.secrets.esphome.restartUnits = [ config.systemd.services.esphome.name ]; + + jalr.esphome = { + enable = true; + port = ports.esphome.tcp; + secretsFile = config.sops.secrets.esphome.path; + configDir = pkgs.stdenvNoCC.mkDerivation { + name = "esphome-config"; + src = ./devices; + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; + }; + }; +} diff --git a/hosts/iron/services/esphome/devices/.env b/hosts/iron/services/esphome/devices/.env new file mode 100644 index 0000000..10c24e2 --- /dev/null +++ b/hosts/iron/services/esphome/devices/.env @@ -0,0 +1,2 @@ +ESPHOME_HOST="jalr-bw.duckdns.org" +ESPHOME_SECRETS_FILE="esphome_${ESPHOME_HOST}_secrets.yaml" diff --git a/hosts/iron/services/esphome/devices/.gitignore b/hosts/iron/services/esphome/devices/.gitignore new file mode 100644 index 0000000..163bec7 --- /dev/null +++ b/hosts/iron/services/esphome/devices/.gitignore @@ -0,0 +1,2 @@ +/.esphome/ +/secrets.yaml diff --git a/hosts/iron/services/esphome/devices/README.md b/hosts/iron/services/esphome/devices/README.md new file mode 100644 index 0000000..4b38621 --- /dev/null +++ b/hosts/iron/services/esphome/devices/README.md @@ -0,0 +1,12 @@ +## Generating passwords + + +Generate home-assistant API key +```sh +dd if=/dev/random bs=32 count=1 | base64 +``` + +Generate OTA secret +```sh +pwgen 14 1 +``` diff --git a/hosts/iron/services/esphome/devices/badspiegel.yaml b/hosts/iron/services/esphome/devices/badspiegel.yaml new file mode 100644 index 0000000..7f262ed --- /dev/null +++ b/hosts/iron/services/esphome/devices/badspiegel.yaml @@ -0,0 +1,134 @@ +esphome: + name: "badspiegel" + friendly_name: "Badspiegel" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.turn_on: + id: front_light + brightness: 30% + color_temperature: 2700 K + - light.turn_on: + id: background_light + brightness: 20% + color_temperature: 2700 K + +esp32: + board: az-delivery-devkit-v4 + framework: + type: arduino + version: recommended + platform_version: 5.4.0 + +logger: + +api: + encryption: + key: !secret apikey_badspiegel + +ota: + - platform: esphome + password: !secret otapass_badspiegel + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + power_save_mode: none + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +output: + - platform: ledc + pin: GPIO33 + id: output_background_warm + - platform: ledc + pin: GPIO32 + id: output_background_cold + - platform: ledc + pin: GPIO25 + id: output_front_warm + - platform: ledc + pin: GPIO14 + id: output_front_cold + +light: + - platform: cwww + name: "Background light" + id: background_light + cold_white: output_background_cold + warm_white: output_background_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + - platform: cwww + name: "Front light" + id: front_light + cold_white: output_front_cold + warm_white: output_front_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + +switch: + - platform: gpio + name: "Heating" + id: heating + pin: GPIO26 + icon: "mdi:thermometer" + - platform: gpio + name: "Soundsystem" + id: soundsystem + pin: GPIO23 + icon: "mdi:speaker" + +esp32_touch: +# setup_mode: true + + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + pin: GPIO22 + model: DHT22 + temperature: + name: "Temperatur" + id: temperature + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 30s + +binary_sensor: +# ESP32 touch pins: 4, 13, 27, 32, 33 + # Touch Pad T4 + - platform: esp32_touch + name: "touch pad GPIO13" + pin: GPIO13 + threshold: 902 + on_press: + then: + - light.toggle: + id: front_light + internal: true + filters: + settle: 1s + # Touch Pad T7 + - platform: esp32_touch + name: "touch pad GPIO27" + pin: GPIO27 + threshold: 1086 + on_press: + then: + - light.toggle: + id: background_light + internal: true + filters: + settle: 1s diff --git a/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml new file mode 100644 index 0000000..88224a9 --- /dev/null +++ b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml @@ -0,0 +1,60 @@ +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +bp5758d: + data_pin: GPIO4 + clock_pin: GPIO5 + +output: + - platform: bp5758d + id: output_ch1 + channel: 1 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch2 + channel: 2 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch3 + channel: 3 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch4 + channel: 4 + current: 80 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch5 + channel: 5 + current: 80 + min_power: 0.02 + zero_means_zero: true + +light: + - platform: rgbww + name: Deckenleuchte + id: ceiling_light + red: output_ch1 + green: output_ch2 + blue: output_ch3 + warm_white: output_ch4 + cold_white: output_ch5 + warm_white_color_temperature: 2700 K + cold_white_color_temperature: 6500 K + color_interlock: true diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py b/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py new file mode 100644 index 0000000..6610048 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@jalr"] diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp new file mode 100644 index 0000000..9dd06da --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp @@ -0,0 +1,155 @@ +#include "miele_w433.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace miele_w433 { + +static const char *const TAG = "miele_w433"; + +static const std::string OPERATION_STATES[] = {"inaktiv", "Einw./Vorwäsche", "Waschen", "Spülen", "Spülstop", "Pumpen", "Endschleudern", "Knitterschutz/Ende"}; + +void MieleW433Sensor::setup() { + this->pin_clock_->setup(); + this->store_.pin_clock = this->pin_clock_->to_isr(); + this->pin_clock_->attach_interrupt(MieleW433SensorStore::gpio_intr_clock, &this->store_, gpio::INTERRUPT_RISING_EDGE); + + this->pin_data_->setup(); + this->store_.pin_data = this->pin_data_; + + if (this->pin_en_lamps_ != nullptr) { + this->pin_en_lamps_->setup(); + this->store_.pin_en_lamps = this->pin_en_lamps_->to_isr(); + this->pin_en_lamps_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_lamps, &this->store_, gpio::INTERRUPT_RISING_EDGE); + } + if (this->pin_en_7seg_ != nullptr) { + this->pin_en_7seg_->setup(); + this->store_.pin_en_7seg = this->pin_en_7seg_->to_isr(); + this->pin_en_7seg_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_7seg, &this->store_, gpio::INTERRUPT_RISING_EDGE); + } +} + +void MieleW433Sensor::loop() { + if (millis() - this->last_update_ > 1000) { + uint32_t dpy_7seg; + uint32_t dpy_lamps; + { + InterruptLock lock; + dpy_7seg = this->store_.dpy_7seg; + dpy_lamps = this->store_.dpy_lamps; + } + uint8_t minutes = (dpy_7seg & 0xf) * 60; + minutes += ((dpy_7seg>>8) &0xf); + minutes += ((dpy_7seg>>4) & 0xf) * 10; + if (this->time_remaining_sensor_ != nullptr) { + this->time_remaining_sensor_->publish_state(minutes); + } + + uint8_t state = 0; + bool is_active = false; + + switch(dpy_lamps & 0x1f007) { + case 1<<0: + // Pumpen + state = 5; + is_active = true; + break; + case 1<<1: + // Endschleudern + state = 6; + is_active = true; + break; + case 1<<2: + // Knitterschutz/Ende + state = 7; + break; + case 1<<12: + // Einw./Vorwäsche + state = 1; + is_active = true; + break; + case 1<<13: + // Waschen + state = 2; + is_active = true; + break; + case 1<<14: + // Spülen + state = 3; + is_active = true; + break; + case 1<<15: + // Spülstop + state = 4; + is_active = true; + break; + } + + if (this->current_operation_sensor_ != nullptr) { + this->current_operation_sensor_->publish_state(OPERATION_STATES[state]); + } + + if (this->active_sensor_ != nullptr) { + this->active_sensor_->publish_state(is_active); + } + + this->last_update_ = millis(); + } +} + +void MieleW433Sensor::dump_config() { + LOG_SENSOR("", "Miele W433", this); + LOG_PIN(" Pin Clock: ", this->pin_clock_); + LOG_PIN(" Pin Data: ", this->pin_data_); + LOG_PIN(" Pin Enable 7-Segment: ", this->pin_en_7seg_); + LOG_PIN(" Pin Enable Lamps: ", this->pin_en_lamps_); +}; + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_clock(MieleW433SensorStore *arg) { + if (!arg->pin_en_7seg.digital_read()) { + if (arg->count_cycles_7seg <= 31) { + if (arg->pin_data->digital_read()) { + arg->tmp_7seg |= (1<<(31-arg->count_cycles_7seg)); + } + else { + arg->tmp_7seg &= ~(1<<(31-arg->count_cycles_7seg)); + } + arg->count_cycles_7seg++; + } + } + if (!arg->pin_en_lamps.digital_read()) { + if (arg->count_cycles_lamps <= 31) { + if (arg->pin_data->digital_read()) { + arg->tmp_lamps |= (1<<(31-arg->count_cycles_lamps)); + } + else { + arg->tmp_lamps &= ~(1<<(31-arg->count_cycles_lamps)); + } + arg->count_cycles_lamps++; + } + } +} + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_lamps(MieleW433SensorStore *arg) { + if (arg->count_cycles_lamps == 8) { + arg->ctrl_lamps = arg->tmp_lamps>>24; + } + else if (arg->count_cycles_lamps == 24) { + arg->dpy_lamps = arg->tmp_lamps>>8; + } + + arg->count_cycles_lamps = 0; +} + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_7seg(MieleW433SensorStore *arg) { + if (arg->count_cycles_7seg == 8) { + arg->ctrl_7seg = arg->tmp_7seg>>24; + } + else if (arg->count_cycles_7seg == 24) { + arg->dpy_7seg = arg->tmp_7seg>>8; + } + arg->count_cycles_7seg = 0; +} + +} // namespace miele_w433 +} // namespace esphome diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h new file mode 100644 index 0000000..55c1f38 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h @@ -0,0 +1,62 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome { +namespace miele_w433 { + + struct MieleW433SensorStore { + ISRInternalGPIOPin pin_clock; + InternalGPIOPin *pin_data; + ISRInternalGPIOPin pin_en_7seg; + ISRInternalGPIOPin pin_en_lamps; + + static void gpio_intr_clock(MieleW433SensorStore *arg); + static void gpio_intr_en_7seg(MieleW433SensorStore *arg); + static void gpio_intr_en_lamps(MieleW433SensorStore *arg); + + volatile int count_cycles_lamps = 0; + volatile int count_cycles_7seg = 0; + volatile uint32_t tmp_lamps; + volatile uint32_t tmp_7seg; + volatile uint32_t dpy_lamps; + volatile uint8_t ctrl_lamps; + volatile uint32_t dpy_7seg; + volatile uint8_t ctrl_7seg; + }; + + class MieleW433Sensor : public sensor::Sensor, public Component { + public: + void set_pin_clock(InternalGPIOPin *pin_clock) { pin_clock_ = pin_clock; } + void set_pin_data(InternalGPIOPin *pin_data) { pin_data_ = pin_data; } + void set_pin_en_7seg(InternalGPIOPin *pin_en_7seg) { pin_en_7seg_ = pin_en_7seg; } + void set_pin_en_lamps(InternalGPIOPin *pin_en_lamps) { pin_en_lamps_ = pin_en_lamps; } + void setup() override; + void loop() override; + void dump_config() override; + + void set_time_remaining_sensor(sensor::Sensor *sensor) { this->time_remaining_sensor_ = sensor; } + void set_active_sensor(binary_sensor::BinarySensor *sensor) { this->active_sensor_ = sensor; } + void set_current_operation_sensor(text_sensor::TextSensor *sensor) { this->current_operation_sensor_ = sensor; } + + protected: + InternalGPIOPin *pin_clock_; + InternalGPIOPin *pin_data_; + InternalGPIOPin *pin_en_7seg_{nullptr}; + InternalGPIOPin *pin_en_lamps_{nullptr}; + MieleW433SensorStore store_{}; + uint32_t last_update_ = 0; + sensor::Sensor *time_remaining_sensor_{nullptr}; + binary_sensor::BinarySensor *active_sensor_{nullptr}; + text_sensor::TextSensor *current_operation_sensor_{nullptr}; + + //enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE}; + }; + +} // namespace miele_w433 +} // namespace esphome diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py b/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py new file mode 100644 index 0000000..2881286 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py @@ -0,0 +1,88 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import binary_sensor, sensor, text_sensor +from esphome.const import ( + CONF_ACTIVE, + CONF_BEEPER, + CONF_CLOCK_PIN, + CONF_CURRENT_OPERATION, + CONF_DATA_PIN, + CONF_DURATION, + CONF_ID, + CONF_STATUS, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_RUNNING, + DEVICE_CLASS_SWITCH, + ICON_ROTATE_RIGHT, + ICON_TIMELAPSE, + UNIT_MINUTE, +) + +CONF_EN_7SEG_PIN = "enable_7segment_pin" +CONF_EN_LAMPS_PIN = "enable_lamps_pin" +CONF_TIME_REMAINING = "time_remaining" +CONF_WATER_INLET = "water_inlet" +CONF_WATER_OUTLET = "water_outlet" +ICON_PLAY = "mdi:play" +ICON_PLAYLIST_PLAY = "mdi:playlist-play" +ICON_WATER_OFF = "mdi:water-off" + +AUTO_LOAD = ["text_sensor", "binary_sensor"] +DEPENDENCIES = [] + +miele_w433_ns = cg.esphome_ns.namespace("miele_w433") +MieleW433Sensor = miele_w433_ns.class_("MieleW433Sensor", sensor.Sensor, cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MieleW433Sensor), + cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Optional(CONF_EN_7SEG_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_EN_LAMPS_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_TIME_REMAINING): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMELAPSE, + accuracy_decimals=0, + ), + cv.Optional(CONF_ACTIVE): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_RUNNING, icon=ICON_PLAY + ), + cv.Optional(CONF_CURRENT_OPERATION): text_sensor.text_sensor_schema( + icon=ICON_PLAYLIST_PLAY + ), + # cv.Optional(CONF_WATER_INLET): binary_sensor.binary_sensor_schema( + # device_class=DEVICE_CLASS_PROBLEM, + # icon=ICON_WATER_OFF + # ), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable( + config[CONF_ID], + ) + await cg.register_component(var, config) + + pin_clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_pin_clock(pin_clock)) + pin_data = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_pin_data(pin_data)) + if CONF_EN_7SEG_PIN in config: + pin_en_7seg = await cg.gpio_pin_expression(config[CONF_EN_7SEG_PIN]) + cg.add(var.set_pin_en_7seg(pin_en_7seg)) + if CONF_TIME_REMAINING in config: + sens = await sensor.new_sensor(config[CONF_TIME_REMAINING]) + cg.add(var.set_time_remaining_sensor(sens)) + if CONF_EN_LAMPS_PIN in config: + pin_en_lamps = await cg.gpio_pin_expression(config[CONF_EN_LAMPS_PIN]) + cg.add(var.set_pin_en_lamps(pin_en_lamps)) + if CONF_ACTIVE in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_ACTIVE]) + cg.add(var.set_active_sensor(sens)) + if CONF_CURRENT_OPERATION in config: + sens = await text_sensor.new_text_sensor(config[CONF_CURRENT_OPERATION]) + cg.add(var.set_current_operation_sensor(sens)) diff --git a/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml new file mode 100644 index 0000000..b39f719 --- /dev/null +++ b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml @@ -0,0 +1,25 @@ +<<: !include chinafrickeldeckenleuchte.yaml + +esphome: + name: "eingang-deckenleuchte" + friendly_name: "Eingang Deckenleuchte" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_eingang_deckenleuchte + +ota: + - platform: esphome + password: !secret otapass_eingang_deckenleuchte + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de diff --git a/hosts/iron/services/esphome/devices/fussbodenheizung.yaml b/hosts/iron/services/esphome/devices/fussbodenheizung.yaml new file mode 100644 index 0000000..0e9019a --- /dev/null +++ b/hosts/iron/services/esphome/devices/fussbodenheizung.yaml @@ -0,0 +1,195 @@ +esphome: + name: "fussbodenheizung" + friendly_name: "Fußbodenheizung" + +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +api: + encryption: + key: !secret apikey_fussbodenheizung + +ota: + - platform: esphome + password: !secret otapass_fussbodenheizung + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + +switch: + - platform: gpio + pin: + number: 16 + inverted: true + id: relay_1 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 14 + inverted: true + id: relay_2 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 12 + inverted: true + id: relay_3 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 13 + inverted: true + id: relay_4 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 0 + inverted: true + id: relay_5 + icon: "mdi:electric-switch" + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + model: DHT22 + pin: GPIO2 + temperature: + name: "Temperatur" + id: temperature_local + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 60s + - platform: homeassistant + id: temperature_kitchen + entity_id: sensor.kueche_leiste_temperatur + - platform: homeassistant + id: humidity_kitchen + entity_id: sensor.kueche_leiste_feuchtigkeit + - platform: homeassistant + id: temperature_bathroom + entity_id: sensor.badspiegel_temperatur + - platform: homeassistant + id: humidity_bathroom + entity_id: sensor.badspiegel_feuchtigkeit + - platform: homeassistant + entity_id: sensor.bthome_sensor_e8e8_temperature + id: temperature_bedroom + - platform: homeassistant + entity_id: sensor.bthome_sensor_e8e8_humidity + id: humidity_bedroom + +climate: + - platform: thermostat + name: "Bad" + sensor: temperature_bathroom + humidity_sensor: humidity_bathroom + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_1 + idle_action: + - switch.turn_off: relay_1 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: morgens + default_target_temperature_low: 23 °C + - name: tag + default_target_temperature_low: 20 °C + - name: nacht + default_target_temperature_low: 18 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Schlafzimmer" + sensor: temperature_bedroom + humidity_sensor: humidity_bedroom + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_2 + idle_action: + - switch.turn_off: relay_2 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 22 °C + - name: nacht + default_target_temperature_low: 18 °C + - name: abwesend + default_target_temperature_low: 15 °C + - platform: thermostat + name: "West" + sensor: temperature_kitchen + humidity_sensor: humidity_kitchen + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_3 + idle_action: + - switch.turn_off: relay_3 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 19 °C + - name: nacht + default_target_temperature_low: 17 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Mitte" + sensor: temperature_local + humidity_sensor: humidity + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_4 + idle_action: + - switch.turn_off: relay_4 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 19 °C + - name: nacht + default_target_temperature_low: 17 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Ost" + sensor: temperature_local # FIXME + #humidity_sensor: + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_5 + idle_action: + - switch.turn_off: relay_5 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 21 °C + - name: nacht + default_target_temperature_low: 19 °C + - name: abwesend + default_target_temperature_low: 16 °C diff --git a/hosts/iron/services/esphome/devices/justfile b/hosts/iron/services/esphome/devices/justfile new file mode 120000 index 0000000..68d1c45 --- /dev/null +++ b/hosts/iron/services/esphome/devices/justfile @@ -0,0 +1 @@ +../../../../../modules/esphome/devices/justfile \ No newline at end of file diff --git a/hosts/iron/services/esphome/devices/kueche-leiste.yaml b/hosts/iron/services/esphome/devices/kueche-leiste.yaml new file mode 100644 index 0000000..996c412 --- /dev/null +++ b/hosts/iron/services/esphome/devices/kueche-leiste.yaml @@ -0,0 +1,116 @@ +esphome: + name: "kueche-leiste" + friendly_name: "Küche Leiste" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.control: + id: led_light + brightness: 50% + color_temperature: 2700 K + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_kueche_leiste + +ota: + - platform: esphome + password: !secret otapass_kueche_leiste + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + +esp32_ble_tracker: + scan_parameters: + active: false + +bluetooth_proxy: + active: true + +xiaomi_ble: + +output: + - platform: ledc + pin: GPIO0 + id: output_warm + - platform: ledc + pin: GPIO1 + id: output_cold + +light: + - platform: cwww + name: "LED light" + id: led_light + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + model: DHT22 + pin: GPIO5 + temperature: + name: "Temperatur" + id: temperature + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 60s + +binary_sensor: + - platform: gpio + pin: + number: GPIO6 + mode: + input: true + pullup: true + inverted: true + name: "Physical Power Button" + on_press: + then: + - light.toggle: + id: led_light + internal: True + +spi: + - id: spi_bus_main + clk_pin: GPIO21 + mosi_pin: GPIO10 + miso_pin: GPIO20 + +pn532_spi: + spi_id: spi_bus_main + # FIXME: GPIO9 is a strapping pin + cs_pin: GPIO9 + update_interval: 1s + on_tag: + then: + - homeassistant.tag_scanned: !lambda 'return x;' + - switch.turn_on: buzzer + - delay: 250ms + - switch.turn_off: buzzer + +switch: + - platform: gpio + pin: GPIO7 + id: buzzer diff --git a/hosts/iron/services/esphome/devices/kuechentisch.yaml b/hosts/iron/services/esphome/devices/kuechentisch.yaml new file mode 100644 index 0000000..5d0f27a --- /dev/null +++ b/hosts/iron/services/esphome/devices/kuechentisch.yaml @@ -0,0 +1,111 @@ +esphome: + name: "kuechentisch" + friendly_name: "Küchentisch" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_temperature: 2700 K + +esp32: + board: esp32dev + framework: + type: esp-idf + sdkconfig_options: + CONFIG_FREERTOS_UNICORE: y + advanced: + ignore_efuse_mac_crc: true + +logger: + +api: + encryption: + key: !secret apikey_kuechentisch + +ota: + - platform: esphome + password: !secret otapass_kuechentisch + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + fast_connect: true + manual_ip: + static_ip: 10.20.0.10 + gateway: 10.20.0.1 + subnet: 255.255.240.0 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO19 + id: output_warm + power_supply: power + min_power: 0.13 + max_power: 0.77 + frequency: 1220Hz + zero_means_zero: true + - platform: ledc + pin: GPIO21 + id: output_cold + power_supply: power + frequency: 1220Hz + zero_means_zero: true + min_power: 0.13 + max_power: 0.76 + + - platform: ledc + pin: GPIO23 + id: output_nightlight + power_supply: power + + - platform: ledc + pin: GPIO33 + id: output_red + power_supply: power + - platform: ledc + pin: GPIO26 + id: output_green + power_supply: power + - platform: ledc + pin: GPIO27 + id: output_blue + power_supply: power + +power_supply: + - id: power + pin: GPIO22 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: monochromatic + name: "night light" + id: night_light + output: output_nightlight + gamma_correct: 0 + on_turn_on: + - light.turn_off: ceiling_light + - platform: cwww + name: "ceiling light" + id: ceiling_light + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + on_turn_on: + - light.turn_off: night_light + - platform: rgb + name: "ambient light" + red: output_red + green: output_green + blue: output_blue + gamma_correct: 0 diff --git a/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml new file mode 100644 index 0000000..ceff0b0 --- /dev/null +++ b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml @@ -0,0 +1,74 @@ +esphome: + name: "led-panel-schreibtisch" + friendly_name: "LED Panel Schreibtisch" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.turn_on: + id: panel + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_panel_schreibtisch + +ota: + - platform: esphome + password: !secret otapass_panel_schreibtisch + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + power_save_mode: none + enable_on_boot: True + fast_connect: true + manual_ip: + static_ip: 10.20.0.11 + gateway: 10.20.0.1 + subnet: 255.255.240.0 + output_power: 8.5 + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO1 + id: output_warm + power_supply: power + frequency: 2kHz + - platform: ledc + pin: GPIO3 + id: output_cold + power_supply: power + frequency: 2kHz + +power_supply: + - id: power + pin: GPIO0 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: cwww + name: "panel" + id: panel + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + gamma_correct: 0 diff --git a/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml b/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml new file mode 100644 index 0000000..4c7af8b --- /dev/null +++ b/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml @@ -0,0 +1,60 @@ +esphome: + name: "pflanzenleuchte" + friendly_name: "Pflanzenleuchte" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_pflanzenleuchte + +ota: + - platform: esphome + password: !secret otapass_pflanzenleuchte + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO0 + id: output_royal_blue + inverted: true + min_power: 0.25 + zero_means_zero: true + - platform: ledc + pin: GPIO1 + id: output_deep_red + inverted: true + min_power: 0.25 + zero_means_zero: true + +light: + - platform: monochromatic + name: "Royalblau" + id: royal_blue + output: output_royal_blue + gamma_correct: false + - platform: monochromatic + name: "Tiefrot" + id: deep_red + output: output_deep_red + gamma_correct: false diff --git a/hosts/iron/services/esphome/devices/pinspot.yaml b/hosts/iron/services/esphome/devices/pinspot.yaml new file mode 100644 index 0000000..b7adc49 --- /dev/null +++ b/hosts/iron/services/esphome/devices/pinspot.yaml @@ -0,0 +1,53 @@ +esphome: + name: "pinspot" + friendly_name: "Pinspot" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_pinspot + +ota: + - platform: esphome + password: !secret otapass_pinspot + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO1 + id: output_led_brightness + min_power: 0.028 + zero_means_zero: true + - platform: ledc + pin: GPIO4 + inverted: true + id: output_led_colortemp + +light: + - platform: color_temperature + name: "Pinspot" + color_temperature: output_led_colortemp + brightness: output_led_brightness + cold_white_color_temperature: 6000 K + warm_white_color_temperature: 2700 K diff --git a/hosts/iron/services/esphome/devices/shroombox.yaml b/hosts/iron/services/esphome/devices/shroombox.yaml new file mode 100644 index 0000000..4f4de46 --- /dev/null +++ b/hosts/iron/services/esphome/devices/shroombox.yaml @@ -0,0 +1,128 @@ +esphome: + name: "shroombox" + friendly_name: "shroombox" + +esp32: + board: esp-wrover-kit + +api: + encryption: + key: !secret apikey_shroombox + +ota: + - platform: esphome + password: !secret otapass_shroombox + +ethernet: + type: LAN8720 + mdc_pin: GPIO23 + mdio_pin: GPIO18 + clk_mode: GPIO0_IN + phy_addr: 1 + power_pin: GPIO16 + +logger: + +i2c: + sda: GPIO4 + scl: GPIO14 + scan: true + +sensor: + - platform: scd30 + id: scd30_sensor + co2: + name: "Shroombox CO2" + accuracy_decimals: 1 + temperature: + name: "Shroombox Temperature" + accuracy_decimals: 2 + humidity: + name: "Shroombox Humidity" + accuracy_decimals: 1 + id: humidity + address: 0x61 + update_interval: 5s + automatic_self_calibration: false + + - platform: hx711 + name: "Water tank weight" + dout_pin: GPIO32 + clk_pin: GPIO33 + gain: 128 + update_interval: 5s + filters: + - calibrate_linear: + - -35884 -> 0 + - 334800 -> 887 + unit_of_measurement: g + - platform: pulse_counter + pin: + number: GPIO36 + #mode: INPUT_PULLUP + mode: INPUT + unit_of_measurement: 'RPM' + id: fan_rpm + name: Fan Speed + accuracy_decimals: 0 + +output: + - platform: ledc + id: fan_duty + pin: GPIO15 + frequency: "25000 Hz" + min_power: 10% + max_power: 100% + zero_means_zero: true + +fan: + - platform: speed + output: fan_duty + name: "Fan" + id: fan1 + +switch: + - platform: gpio + pin: GPIO2 + id: humidifier + name: "Humidifier" + +number: + - platform: template + name: "CO2 calibration value" + optimistic: true + min_value: 350 + max_value: 4500 + step: 1 + id: co2_cal + icon: "mdi:molecule-co2" + entity_category: "config" + +button: + - platform: template + name: "SCD30 Force manual calibration" + entity_category: "config" + on_press: + then: + - scd30.force_recalibration_with_reference: + value: !lambda 'return id(co2_cal).state;' + +climate: + - platform: thermostat + name: "Humidistat" + sensor: humidity + min_idle_time: 20s + min_heating_off_time: 60s + min_heating_run_time: 60s + visual: + min_temperature: 0 + max_temperature: 100 + preset: + - name: default + mode: heat + default_target_temperature_low: 80 + + heat_action: + - switch.turn_on: humidifier + idle_action: + - switch.turn_off: humidifier diff --git a/hosts/iron/services/esphome/devices/tuerschloss.yaml b/hosts/iron/services/esphome/devices/tuerschloss.yaml new file mode 100644 index 0000000..1560071 --- /dev/null +++ b/hosts/iron/services/esphome/devices/tuerschloss.yaml @@ -0,0 +1,85 @@ +esphome: + name: "tuerschloss" + friendly_name: "Türschloss" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_tuerschloss + +ota: + - platform: esphome + password: !secret otapass_tuerschloss + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: gpio + pin: + number: GPIO0 + inverted: true + mode: + output: true + open_drain: true + id: btn_unlock + - platform: gpio + pin: + number: GPIO1 + inverted: true + mode: + output: true + open_drain: true + id: btn_lock + - platform: gpio + pin: + number: GPIO3 + id: btn_open + +lock: + - platform: template + name: "Türschloss" + id: tuerschloss + assumed_state: true + lock_action: + - output.turn_on: btn_lock + - delay: 250ms + - output.turn_off: btn_lock + - delay: 5s + - lambda: id(tuerschloss).publish_state(LOCK_STATE_LOCKED); + unlock_action: + - output.turn_on: btn_unlock + - delay: 250ms + - output.turn_off: btn_unlock + - delay: 5s + - lambda: id(tuerschloss).publish_state(LOCK_STATE_UNLOCKED); + +button: + - platform: template + name: "Türöffner" + id: tueroeffner + icon: mdi:lock-open + on_press: + - logger.log: "Button pressed" + - output.turn_on: btn_open + - delay: 250ms + - output.turn_off: btn_open diff --git a/hosts/iron/services/esphome/devices/tuersprechanlage.yaml b/hosts/iron/services/esphome/devices/tuersprechanlage.yaml new file mode 100644 index 0000000..d40d2c4 --- /dev/null +++ b/hosts/iron/services/esphome/devices/tuersprechanlage.yaml @@ -0,0 +1,124 @@ +esphome: + name: "tuersprechanlage" + friendly_name: "Türsprechanlage" + +esp32: + board: lolin_s2_mini + variant: ESP32S2 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_ESP_CONSOLE_USB_CDC: y + +logger: + hardware_uart: USB_CDC + +api: + encryption: + key: !secret apikey_tuersprechanlage + +ota: + - platform: esphome + password: !secret otapass_tuersprechanlage + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: True + power_save_mode: none + output_power: 10 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +binary_sensor: + - platform: gpio + name: Etagenklingel + id: floor_bell + pin: + number: GPIO16 + mode: + input: true + pullup: true + filters: + - delayed_off: 10s + + - platform: gpio + name: Treppenlicht + id: staircase_light + pin: + number: GPIO18 + mode: + input: true + pullup: true + filters: + - invert + - delayed_off: 10s + on_press: + then: + - output.turn_on: output_staircase_light_ssr + - delay: 200ms + - output.turn_off: output_staircase_light_ssr + +output: + - platform: gpio + pin: GPIO15 + id: output_door_opener + - platform: ledc + pin: + number: GPIO33 + inverted: true + id: output_staircase_light_ssr + frequency: 50000 + min_power: 0 + max_power: 0.12 + - platform: template + type: binary + id: output_staircase_light_permanent + write_action: + - if: + condition: + lambda: 'return state;' + then: + - script.execute: script_staircase_permanent + else: + - script.stop: script_staircase_permanent + +button: + - platform: template + name: "Türöffner" + id: btn_tueroeffner + icon: mdi:lock-open + on_press: + - output.turn_on: output_door_opener + - delay: 500ms + - output.turn_off: output_door_opener + - platform: template + name: "Treppenlicht" + id: btn_staircase_light + icon: mdi:stairs + on_press: + - output.turn_on: output_staircase_light_ssr + - delay: 200ms + - output.turn_off: output_staircase_light_ssr + +light: + - platform: binary + id: staircase_permanent + name: Treppenlicht permanent + output: output_staircase_light_permanent + +script: + - id: script_staircase_permanent + mode: restart + then: + - while: + condition: + lambda: 'return true;' + then: + - button.press: btn_staircase_light + - delay: 90s diff --git a/hosts/iron/services/esphome/devices/waschmaschine.yaml b/hosts/iron/services/esphome/devices/waschmaschine.yaml new file mode 100644 index 0000000..fad1aea --- /dev/null +++ b/hosts/iron/services/esphome/devices/waschmaschine.yaml @@ -0,0 +1,124 @@ +esphome: + name: "waschmaschine" + friendly_name: "Waschmaschine" + +api: + encryption: + key: !secret apikey_waschmaschine + +ota: + - platform: esphome + password: !secret otapass_waschmaschine + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + +esp32: + board: esp32doit-devkit-v1 + framework: + type: arduino + version: recommended + +logger: + +external_components: + - source: components + components: [ miele_w433 ] + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: miele_w433 + enable_7segment_pin: 27 + clock_pin: 14 + enable_lamps_pin: 26 + data_pin: 13 + time_remaining: + name: "verbleibende Zeit" + active: + name: "in Betrieb" + current_operation: + name: "aktueller Vorgang" + id: current_operation + +number: + - platform: template + name: "Waschmittelmenge" + id: detergent_dosing + min_value: 25.0 + max_value: 150.0 + step: 1 + unit_of_measurement: "ml" + icon: 'mdi:cup-water' + restore_value: true + initial_value: 75.0 + optimistic: true + - platform: template + name: "Waschmittelvorrat" + id: detergent_supply + min_value: 0.0 + max_value: 5000 + step: 1.0 + unit_of_measurement: "ml" + icon: 'mdi:cup-water' + restore_value: true + initial_value: 0 + optimistic: true + +stepper: + - platform: a4988 + id: detergent_stepper + step_pin: GPIO32 + sleep_pin: GPIO33 + dir_pin: GPIO25 # not used + max_speed: 600 steps/s + acceleration: 125 steps/s^2 + deceleration: 125 steps/s^2 + +globals: + - id: dosing_enabled + type: bool + restore_value: false + initial_value: 'true' + +interval: + - interval: 1s + then: + if: + condition: + and: + #- lambda: return id(current_operation).state == "Einw/Vorwäsche"; + - lambda: return id(current_operation).state == "Waschen"; + - lambda: return id(dosing_enabled); + then: + - lambda: &dosing |- + float dose = id(detergent_dosing).state; + float current_supply = id(detergent_supply).state; + if (current_supply >= dose) { + id(detergent_stepper).set_target(dose * 125); + id(detergent_stepper).report_position(0); + id(detergent_supply).publish_state(current_supply - dose); + ESP_LOGD("custom", "Waschmitteldosierung durchgeführt: %.2f ml, verbleibender Vorrat: %.2f ml", dose, current_supply - dose); + } else { + ESP_LOGW("custom", "Nicht genug Waschmittelvorrat! Aktueller Vorrat: %.2f ml, gewünschte Dosierung: %.2f ml", current_supply, dose); + } + - lambda: |- + id(dosing_enabled) = false; + - interval: 1s + then: + if: + condition: + lambda: return id(current_operation).state == "Knitterschutz/Ende"; + then: + - lambda: |- + id(dosing_enabled) = true; + +button: + - platform: template + name: "Waschmitteldosierung auslösen" + icon: "mdi:cup-water" + on_press: + - lambda: *dosing diff --git a/hosts/iron/services/esphome/devices/water-bottle.yaml b/hosts/iron/services/esphome/devices/water-bottle.yaml new file mode 100644 index 0000000..456b973 --- /dev/null +++ b/hosts/iron/services/esphome/devices/water-bottle.yaml @@ -0,0 +1,238 @@ +substitutions: + tolerance: "10" + minimum_sip: "20" + default_bottle_tare: "198" + +esphome: + name: "water-bottle" + friendly_name: "Water bottle" + +esp32: + board: lolin_s2_mini + variant: ESP32S2 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_ESP_CONSOLE_USB_CDC: y + +logger: + hardware_uart: USB_CDC + +api: + encryption: + key: !secret apikey_water_bottle + on_client_connected: + - light.turn_on: + id: pixels + effect: "scan" + red: 0% + green: 25% + blue: 0% + - delay: 2s + - light.turn_off: + id: pixels + +ota: + - platform: esphome + password: !secret otapass_water_bottle + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: True + power_save_mode: none + output_power: 10 + +globals: + - id: volume_when_removed + type: int + restore_value: no + initial_value: "NAN" + - id: bottle_returned + type: bool + restore_value: no + initial_value: "false" + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: hx711 + internal: true + id: scale + dout_pin: GPIO16 + clk_pin: GPIO17 + gain: 128 + update_interval: 0.1s + unit_of_measurement: g + filters: + - quantile: + window_size: 10 + send_every: 5 + send_first_at: 5 + quantile: .9 + - calibrate_linear: + - 197155 -> 0 + - 246676 -> 50 + - platform: copy + name: "absolute volume" + id: volume_absolute + icon: "mdi:water" + source_id: scale + filters: + - lambda: "return x - id(bottle_tare).state;" + - clamp: + min_value: 0 + ignore_out_of_range: true + - quantile: + window_size: 5 + send_every: 5 + send_first_at: 5 + quantile: .9 + on_value: + then: + - if: + condition: + lambda: |- + if (id(bottle_returned)) { + id(bottle_returned) = false; + return true; + } + return false; + then: + - lambda: |- + ESP_LOGI("main", "Bottle returned, waiting to settle"); + ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state); + - delay: 3s + - lambda: |- + ESP_LOGI("main", "Bottle settled"); + ESP_LOGI("main", "Volume when removed: %d", id(volume_when_removed)); + ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state); + if (id(volume_when_removed) > id(volume_absolute).state + ${minimum_sip}) { + ESP_LOGI("main", "Volume total before: %f", id(volume_total).state); + auto call = id(volume_total).make_call(); + call.set_value(id(volume_when_removed) - id(volume_absolute).state + id(volume_total).state); + call.perform(); + ESP_LOGI("main", "Volume total now: %f", id(volume_total).state); + } + - platform: template + name: "Water consumption rate" + id: "volume_total_derivative" + unit_of_measurement: "ml/h" + icon: "mdi:water-check" + lambda: |- + static float last_value = 0; + static float last_time = 0; + float time = (float) millis(); + if (last_time == 0){ + last_value = id(volume_total).state; + last_time = time; + return {}; + } + float change = ( ( id(volume_total).state - last_value ) / ( time - last_time ) ) *1000*60*60; + last_value = id(volume_total).state; + last_time = time; + return change; + filters: + - sliding_window_moving_average: + window_size: 3 + send_every: 2 + - or: + - delta: 0.01 + - heartbeat: 120minutes + - throttle: 30s + +number: + - platform: template + name: "Bottle tare" + id: bottle_tare + icon: mdi:weight-gram + optimistic: true + initial_value: "${default_bottle_tare}" + unit_of_measurement: "g" + step: 1 + min_value: 0 + max_value: 1000 + - platform: template + name: "total volume" + id: volume_total + optimistic: true + icon: "mdi:water" + unit_of_measurement: "ml" + step: 1 + min_value: 0 + max_value: 10000 + +binary_sensor: + - platform: template + name: "Bottle present" + id: bottle_present + lambda: |- + if (id(scale).state > id(bottle_tare).state - ${tolerance}) { + return true; + } else { + return false; + } + on_release: + then: + - lambda: |- + id(volume_when_removed) = id(volume_absolute).state; + ESP_LOGI("main", "Volume absolute: %f ml", id(volume_absolute).state); + ESP_LOGI("main", "Bottle removed with %d ml", id(volume_when_removed)); + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 75% + green: 0% + blue: 0% + on_press: + then: + - globals.set: + id: bottle_returned + value: "true" + - if: + condition: + lambda: "return id(volume_absolute).state > 250;" + then: + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 0% + green: 50% + blue: 50% + else: + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 50% + green: 25% + blue: 0% + - delay: 2s + - light.turn_off: + id: pixels + +e131: + method: multicast + +light: + - platform: esp32_rmt_led_strip + id: pixels + rgb_order: GRB + pin: GPIO12 + num_leds: 8 + rmt_channel: 0 + chipset: ws2812 + effects: + - addressable_scan: + name: scan + move_interval: 20ms + scan_width: 1 + - pulse: + name: slow_pulse + transition_length: 500ms + update_interval: 10s + - pulse: + name: normal_pulse + min_brightness: 20% diff --git a/hosts/iron/services/esphome/devices/wohnungstuer.yaml b/hosts/iron/services/esphome/devices/wohnungstuer.yaml new file mode 100644 index 0000000..11c0675 --- /dev/null +++ b/hosts/iron/services/esphome/devices/wohnungstuer.yaml @@ -0,0 +1,253 @@ +esphome: + name: "wohnungstuer" + friendly_name: "Wohnungstür" + +api: + encryption: + key: !secret apikey_wohnungstuer + +ota: + - platform: esphome + password: !secret otapass_wohnungstuer + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + +esp32_ble_tracker: + scan_parameters: + active: false + +bluetooth_proxy: + active: true + +xiaomi_ble: + +esp32: + board: esp32doit-devkit-v1 + framework: + type: arduino + version: recommended + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +globals: + - id: leaving + type: int + restore_value: no + initial_value: '0' + +binary_sensor: + - platform: template + name: "At home" + id: presence + device_class: presence + - platform: gpio + name: Tür + id: door + pin: + number: GPIO32 + mode: + input: true + pullup: true + filters: + - delayed_on_off: 1s + device_class: door + on_press: # on opening door + then: + - lambda: |- + id(leaving) |= 1; + - if: + condition: + # bowl is occupied + binary_sensor.is_on: key_bowl + then: + - light.turn_on: + id: pixels + effect: strobe_red + - script.execute: + id: beep + on_ms: 250 + off_ms: 250 + + else: + - if: + condition: + binary_sensor.is_off: presence # when away + then: + - light.turn_on: + id: pixels + effect: "None" + red: 75% + green: 25% + + on_release: # on closing door + then: + - if: + condition: + binary_sensor.is_off: presence # when away + then: + - light.turn_on: + id: pixels + effect: "scan" + red: 0% + green: 0% + blue: 100% + - script.execute: + id: beep + on_ms: 100 + off_ms: 1000 + - if: + condition: + lambda: |- + return id(leaving) == 3; + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + - binary_sensor.template.publish: # set away state + id: presence + state: OFF + else: + - if: + condition: + # when keys are in the bowl + binary_sensor.is_on: key_bowl + then: + - script.stop: beep + - switch.turn_off: buzzer + - light.turn_on: + id: pixels + effect: "None" + red: 0% + green: 50% + blue: 0% + - delay: 30s + - light.turn_off: + id: pixels + transition_length: 3s + - lambda: |- + id(leaving) = 0; + - if: + condition: + binary_sensor.is_on: presence # when at home + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + + - platform: gpio + name: Schlüsselschale + id: key_bowl + pin: + number: GPIO23 + mode: + input: true + pullup: true + device_class: occupancy + filters: + - invert: + - delayed_on_off: 250ms + on_press: # when keys fall in bowl + then: + - binary_sensor.template.publish: # set at home state + id: presence + state: ON + - lambda: |- + id(leaving) = 0; + - if: + condition: + # door is closed + binary_sensor.is_off: door + then: + - script.stop: beep + - switch.turn_off: buzzer + - light.turn_on: + id: pixels + effect: "None" + red: 0% + green: 50% + blue: 0% + - delay: 30s + - light.turn_off: + id: pixels + transition_length: 3s + on_release: # when keys are removed from bowl + then: + - if: + condition: + lambda: |- + return id(leaving) == 1; + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + - lambda: |- + id(leaving) |= 2; + - if: + condition: + # door is closed + binary_sensor.is_off: door + then: + - light.turn_on: + id: pixels + effect: "rainbow" + +light: + - platform: esp32_rmt_led_strip + id: pixels + rgb_order: GRB + pin: GPIO12 + num_leds: 5 + rmt_channel: 0 + chipset: ws2812 + effects: + - strobe: + name: strobe_red + colors: + - state: true + brightness: 100% + red: 100% + green: 0% + blue: 0% + duration: 250ms + - state: false + duration: 250ms + - addressable_rainbow: + name: rainbow + speed: 20 + width: 10 + - addressable_scan: + name: scan + move_interval: 20ms + scan_width: 1 + +switch: + - platform: gpio + pin: GPIO22 + id: buzzer + +script: + - id: beep + mode: restart + parameters: + on_ms: int + off_ms: int + then: + - while: + condition: + lambda: 'return true;' + then: + - switch.turn_on: buzzer + - delay: !lambda return on_ms; + - switch.turn_off: buzzer + - delay: !lambda return off_ms; diff --git a/hosts/iron/services/grafana.nix b/hosts/iron/services/grafana.nix new file mode 100644 index 0000000..15e1571 --- /dev/null +++ b/hosts/iron/services/grafana.nix @@ -0,0 +1,146 @@ +{ config +, lib +, pkgs +, ... +}: +let + inherit (config.networking) ports; + domain = "grafana.jalr.de"; + cfg = config.services.grafana; +in +{ + sops.secrets = { + "grafana/secret-key" = { + sopsFile = ../secrets.yaml; + owner = config.systemd.services.grafana.serviceConfig.User; + }; + }; + + services.grafana = { + enable = true; + settings = { + server = { + inherit domain; + root_url = "https://%(domain)s"; + http_addr = "127.0.0.1"; + http_port = ports.grafana.tcp; + }; + security = { + content_security_policy = true; + cookie_samesite = "strict"; + cookie_secure = true; + secret_key = "$__file{${config.sops.secrets."grafana/secret-key".path}}"; + strict_transport_security = true; + strict_transport_security_preload = true; + strict_transport_security_subdomains = true; + }; + analytics = { + reporting_enabled = false; + check_for_updates = false; + check_for_plugin_updates = false; + }; + }; + provision = { + datasources.settings = { + apiVersion = 1; + datasources = with config.services.prometheus; + ( + lib.lists.optional enable { + name = "Prometheus"; + type = "prometheus"; + url = "http://${listenAddress}:${toString port}"; + orgId = 1; + } + ) + ++ (with config.services.prometheus.alertmanager; ( + lib.lists.optional enable { + name = "Alertmanager"; + type = "alertmanager"; + url = "http://${listenAddress}:${toString port}"; + orgId = 1; + } + )); + deleteDatasources = [ + { + name = "Prometheus"; + orgId = 1; + } + { + name = "Alertmanager"; + orgId = 1; + } + ]; + }; + + dashboards.settings.providers = + let + # https://grafana.com/grafana/dashboards/ + fetchDashboard = + { name + , hash + , id + , version + , + }: + pkgs.fetchurl { + inherit name hash; + url = "https://grafana.com/api/dashboards/${toString id}/revisions/${toString version}/download"; + recursiveHash = true; + postFetch = '' + mv "$out" temp + mkdir -p "$out" + mv temp "$out/${name}.json"; + ''; + }; + dashboard = name: fetchArgs: { + inherit name; + options.path = fetchDashboard fetchArgs; + }; + in + [ + (dashboard "Node Exporter Full" + { + name = "node-exporter-full"; + hash = "sha256-QTHG9ioy7E8U8O8x/qFabOxK2qBjlGlzuEvwYKug0CQ="; + id = 1860; + version = 36; + }) + (dashboard "Node Exporter" + { + name = "node-exporter"; + hash = "sha256-2xgE0m3SUFiux501uCVb4aH3zGfapW/SmfxRsFC/514="; + id = 13978; + version = 2; + }) + (dashboard "AlertManager" + { + name = "alertmanager"; + hash = "sha256-Yvw0DGQJpqBYNzE4ES/x7ZAYF7iJ4SUNBKB+sJRuGBw="; + id = 9578; + version = 4; + }) + ]; + }; + }; + services.nginx.virtualHosts = { + "${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://${cfg.settings.server.http_addr}:${toString cfg.settings.server.http_port}"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + }; + }; + + environment.persistence."/persist".directories = [ + { + directory = "/var/lib/grafana"; + user = "grafana"; + group = "grafana"; + mode = "u=rwx,g=,o="; + } + ]; +} diff --git a/hosts/iron/services/home-assistant.nix b/hosts/iron/services/home-assistant.nix new file mode 100644 index 0000000..d62f792 --- /dev/null +++ b/hosts/iron/services/home-assistant.nix @@ -0,0 +1,272 @@ +{ lib, pkgs, config, ... }: +let + inherit (config.networking) ports; + interfaces = import ../interfaces.nix; + domain = "hass.jalr.de"; +in +{ + sops.secrets.home-assistant = { + owner = "root"; + group = "hass"; + mode = "0640"; + }; + + networking.firewall.interfaces = { + "${interfaces.lan}".allowedTCPPorts = [ ports.mqtt.tcp ]; + iot.allowedTCPPorts = [ ports.mqtt.tcp ]; + }; + + services = { + home-assistant = { + enable = true; + lovelaceConfig = { + title = "Home"; + views = [ + { + path = "default_view"; + title = "Home"; + cards = [ + { + title = "Eingang"; + type = "entities"; + entities = [ + { + entity = "light.eingang_deckenleuchte_deckenleuchte"; + name = "Deckenleuchte"; + } + ]; + } + { + name = "Esstisch"; + type = "entities"; + entities = [ + { + entity = "light.yeelight_meteorite_ambient_light"; + name = "Ambient light"; + } + { + entity = "light.yeelight_meteorite_ceiling_light"; + name = "Ceiling light"; + } + { + entity = "light.yeelight_meteorite_night_light"; + name = "Night light"; + } + ]; + } + ]; + } + ]; + }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "bthome" + "caldav" + "esphome" + "local_todo" + "openweathermap" + "wyoming" + "xiaomi_ble" + "vlc_telnet" + ]; + customComponents = with pkgs.home-assistant-custom-components; [ + adaptive_lighting + ]; + customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [ + valetudo-map-card + ]; + lovelaceConfigWritable = false; + configWritable = false; + config = { + http = { + server_host = [ "127.0.0.1" ]; + server_port = ports.home-assistant.tcp; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + homeassistant = { + unit_system = "metric"; + time_zone = "Europe/Berlin"; + temperature_unit = "C"; + inherit (config.location) longitude; + inherit (config.location) latitude; + external_url = "https://${domain}/"; + internal_url = "https://${domain}/"; + }; + default_config = { }; + adaptive_lighting = { + lights = [ + "light.yeelight_meteorite_ceiling_light" + "light.eingang_deckenleuchte_deckenleuchte" + "light.led_panel_schreibtisch_panel" + "light.kueche_leiste_led_light" + "light.badspiegel_background_light" + "light.badspiegel_front_light" + ]; + }; + "automation nix" = [ + { + alias = "Waschmaschine fertig Benachrichtigung"; + trigger = { + platform = "state"; + entity_id = "sensor.waschmaschine_aktueller_vorgang"; + to = "Knitterschutz/Ende"; + }; + action = [ + { + service = "notify.mobile_app_shift6mq"; + data = { + message = "Die Waschmaschine hat das Programm beendet."; + title = "Wäsche fertig"; + }; + } + ]; + } + ]; + "automation ui" = "!include automations.yaml"; + "scene nix" = [ + ]; + "scene ui" = "!include scenes.yaml"; + bluetooth = { }; + device_tracker = [ + { + platform = "bluetooth_le_tracker"; + } + ]; + "script nix" = [ + { + lights_off_except = { + icon = "mdi:home-lightbulb"; + fields.exclude_lights.description = "Excluded lights as list"; + sequence = [ + { + service = "logbook.log"; + data_template = { + entity_id = "script.turn_off_lights"; + name = "Exclude log"; + message = "Turning of all lights except: {{ exclude_lights }}"; + }; + } + { + service = "light.turn_off"; + data_template.entity_id = '' + {{ states.light | rejectattr('entity_id', 'in', exclude_lights) | rejectattr('state', 'in', 'off') | join(',', attribute='entity_id') }} + ''; + } + ]; + }; + } + ]; + "script ui" = "!include scripts.yaml"; + calendar = [ + { + platform = "caldav"; + username = "jalr@jalr.de"; + password = "!secret radicale"; + url = "https://cal.jalr.de/radicale"; + } + ]; + mqtt = { }; + media_player = [ + { + platform = "mpd"; + name = "mpd@iron"; + host = "127.0.0.1"; + } + ]; + }; + }; + + mosquitto = { + enable = true; + persistence = true; + listeners = [ + { + port = ports.mqtt.tcp; + users = { + valetudo = { + passwordFile = config.sops.secrets."mqtt-users/valetudo".path; + acl = [ + "readwrite homeassistant/+/donald/#" + "readwrite valetudo/donald/#" + ]; + }; + home-assistant = { + passwordFile = config.sops.secrets."mqtt-users/home-assistant".path; + acl = [ "readwrite #" ]; + }; + }; + } + ]; + }; + }; + + systemd.services = { + home-assistant.serviceConfig.ExecStartPre = [ + ( + pkgs.writeShellScript "home-assistant-secrets" '' + ln -sf "${config.sops.secrets.home-assistant.path}" "${config.services.home-assistant.configDir}/secrets.yaml" + '' + ) + ]; + hass-vlc = { + script = '' + exec ${pkgs.vlc}/bin/cvlc \ + --no-video \ + -I telnet \ + --telnet-password=vlc \ + --sout='#transcode{acodec=s16le,channels=2,samplerate=48000}:std{access=file,mux=raw,dst=/run/snapserver/hass.fifo}' \ + --aout=none + ''; + wants = [ "snapserver.service" ]; + after = [ "snapserver.service" ]; + serviceConfig = { + BindPaths = [ "/run/snapserver/hass.fifo" ]; + BindReadOnlyPaths = [ "/nix/store" "/etc/ssl/certs" "/etc/static/ssl/certs" "/bin/sh" ]; + CapabilityBoundingSet = ""; + DynamicUser = "true"; + Group = "snapserver"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/hass-vlc"; + RuntimeDirectory = "hass-vlc"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; + + systemd.tmpfiles.rules = [ + "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" + "f ${config.services.home-assistant.configDir}/scenes.yaml 0755 hass hass" + ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.home-assistant.tcp}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/iron/services/jellyfin/broflix.svg b/hosts/iron/services/jellyfin/broflix.svg new file mode 100644 index 0000000..7653de9 --- /dev/null +++ b/hosts/iron/services/jellyfin/broflix.svg @@ -0,0 +1,57 @@ + + + + + + + + + unrar and chill + + diff --git a/hosts/iron/services/jellyfin.nix b/hosts/iron/services/jellyfin/default.nix similarity index 71% rename from hosts/iron/services/jellyfin.nix rename to hosts/iron/services/jellyfin/default.nix index c758f74..eb4e918 100644 --- a/hosts/iron/services/jellyfin.nix +++ b/hosts/iron/services/jellyfin/default.nix @@ -1,26 +1,54 @@ -{ lib, pkgs, ... }: +{ config, lib, pkgs, ... }: +let + inherit (config.networking) ports; + logoPng = pkgs.stdenvNoCC.mkDerivation { + name = "broflix.png"; + src = ./broflix.svg; + dontBuild = true; + dontUnpack = true; + installPhase = '' + export PATH="$PATH:${pkgs.lib.makeBinPath [pkgs.imagemagick]}" + convert \ + -background transparent \ + $src \ + -resize 1302x \ + $out + ''; + }; +in { + imports = [ + ./rar2fs.nix + ]; + services.jellyfin = { enable = true; }; + systemd.services.jellyfin = { serviceConfig = { ###MemoryDenyWriteExecute = true; BindPaths = [ + "/dev/dri/renderD128" "/var/cache/jellyfin" "/var/lib/jellyfin" ]; BindReadOnlyPaths = [ - "/nix/store" + "/etc/resolv.conf" + "/etc/ssl" + "/etc/static/ssl" "/filebitch/pub/Filme" "/filebitch/pub/Serien" - "/var/lib/qbittorrent/downloads" + "/nix/store" + "/run/opengl-driver" + "/var/lib/qBittorrent/downloads" ]; CapabilityBoundingSet = ""; + DeviceAllow = "/dev/dri/renderD128 rw"; #IPAddressAllow = "localhost"; #IPAddressDeny = "any"; LockPersonality = true; - PrivateDevices = lib.mkForce true; + PrivateDevices = false; PrivateUsers = true; ProtectClock = true; ProtectControlGroups = true; @@ -57,7 +85,7 @@ # add_header X-XSS-Protection "1; mode=block"; # add_header X-Content-Type-Options "nosniff"; location / { - proxy_pass http://127.0.0.1:8096; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -66,8 +94,17 @@ proxy_set_header X-Forwarded-Host $http_host; proxy_buffering off; } + location = /web/broflix.svg { + alias ${./broflix.svg}; + } + location = /web/assets/img/banner-light.png { + alias ${logoPng}; + } + location = /web/assets/img/banner-dark.png { + alias ${logoPng}; + } location = /web/ { - proxy_pass http://127.0.0.1:8096/web/index.html; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}/web/index.html; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -76,7 +113,7 @@ proxy_set_header X-Forwarded-Host $http_host; } location /socket { - proxy_pass http://127.0.0.1:8096; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; diff --git a/hosts/iron/services/jellyfin/rar2fs.nix b/hosts/iron/services/jellyfin/rar2fs.nix new file mode 100644 index 0000000..dc634cf --- /dev/null +++ b/hosts/iron/services/jellyfin/rar2fs.nix @@ -0,0 +1,62 @@ +{ lib, pkgs, ... }: + +let + rar2fs = pkgs.rar2fs.override { unrar = pkgs.unrar_6; }; + rar2fs_mounts = pkgs.writeScriptBin "rar2fs_mounts" (lib.strings.concatLines [ + "#!${pkgs.python3}/bin/python" + (builtins.readFile ./rar2fs_mounts.py) + ]); + rar_path = "/var/lib/qBittorrent/downloads"; + mount_path = "/run/jellyfin/rar2fs"; +in +{ + programs.fuse = { + userAllowOther = true; + mountMax = 1000; + }; + + environment.systemPackages = [ + rar2fs + ]; + + systemd.services.jellyfin-rar2fs = { + after = [ "jellyfin.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ rar2fs "/run/wrappers/bin" ]; + environment.USER = "jellyfin"; + serviceConfig = { + AmbientCapabilities = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID"; + CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID"; + DeviceAllow = "/dev/fuse rw"; + ExecStart = "${rar2fs_mounts}/bin/rar2fs_mounts ${rar_path} ${mount_path}"; + Group = "jellyfin"; + IPAddressDeny = "any"; + LockPersonality = true; + NoNewPrivileges = "no"; + PrivateDevices = false; + PrivateMounts = false; + PrivateTmp = false; + PrivateUsers = false; + ProtectClock = true; + ProtectControlGroups = false; # implies MountAPIVFS + ProtectHome = false; + ProtectHostname = true; + ProtectKernelLogs = false; + ProtectKernelModules = false; + ProtectKernelTunables = false; # implies MountAPIVFS + #ProtectProc = "noaccess"; # implies MountAPIVFS + ProtectSystem = false; + RestrictAddressFamilies = "none"; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "@mount" + "@setuid" + "umount2" + ]; + User = "jellyfin"; + }; + }; +} diff --git a/hosts/iron/services/jellyfin/rar2fs_mounts.py b/hosts/iron/services/jellyfin/rar2fs_mounts.py new file mode 100644 index 0000000..aacbf38 --- /dev/null +++ b/hosts/iron/services/jellyfin/rar2fs_mounts.py @@ -0,0 +1,112 @@ +from pathlib import Path +import argparse +import errno +import os +import signal +import subprocess +import sys +import time + + +mounts = {} + + +class RarMount: + process = None + + @property + def mountpoint(self): + result = self.mount_root / self.rar_file.relative_to(self.rar_root).parent + return result + + def __init__(self, mount_root: str, rar_file: Path, rar_root: Path): + self.mount_root = mount_root + self.rar_file = rar_file + self.rar_root = rar_root + + os.makedirs(self.mountpoint, exist_ok=True) + + print(f"Mounting '{self.rar_file}' at '{self.mountpoint}'") + self.process = subprocess.Popen( + [ + "rar2fs", + "-f", + "-o", + "auto_unmount", + "-o", + "allow_other", + "--no-inherit-perm", + self.rar_file, + self.mountpoint, + ] + ) + + def __del__(self): + if self.process: + self.process.terminate() + self.process.communicate() + + for i in range(10): + try: + os.rmdir(self.mountpoint) + except FileNotFoundError: + pass + except OSError as ex: + # if ex.errno == errno.ENOEMPTY: + # break + if ex.errno == errno.EBUSY: + time.sleep(1) + raise + else: + break + + for dir in self.mountpoint.relative_to(self.mount_root).parents: + try: + os.rmdir(self.mount_root.joinpath(dir)) + except OSError as ex: + pass + + +def signal_handler(sig, frame): + for rar_file, mount in mounts.items(): + del mount + + sys.exit(0) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Recursively globs a path containing rar files and mounts them under a given mount path." + ) + + parser.add_argument("rar_path", type=Path, help="Path to the RAR directory") + + parser.add_argument("mount_path", type=Path, help="Path to the mount directory") + + return parser.parse_args() + + +def main(): + args = parse_args() + + if not args.rar_path.is_dir(): + parser.error(f"RAR path '{args.rar_path}' is not a valid directory.") + + signal.signal(signal.SIGINT, signal_handler) + + for rar_file in args.rar_path.rglob("*.rar"): + if rar_file in mounts: + continue + if len(rar_file.parts) >= 2 and rar_file.parts[-2].lower() in ["subs", "proof"]: + continue + + mounts[rar_file] = RarMount(args.mount_path, rar_file, args.rar_path) + + while True: + time.sleep(600) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/hosts/iron/services/mail.nix b/hosts/iron/services/mail.nix index 315f46a..c5d855e 100644 --- a/hosts/iron/services/mail.nix +++ b/hosts/iron/services/mail.nix @@ -1,17 +1,15 @@ -{ config, pkgs, ... }: +{ config, ... }: + +let + inherit (config.networking) ports; +in { - sops.secrets.hetzner-api-key = { - sopsFile = ../secrets.yaml; - owner = "acme"; - }; - #sops.secrets."domain_key_jalr.de" = { - # sopsFile = ../secrets.yaml; - # owner = "rspamd"; - #}; + #sops.secrets."domain_key_jalr.de".owner = "rspamd"; jalr = { mailserver = { enable = true; fqdn = "hha.jalr.de"; + relayPort = ports.postfix-relay.tcp; domains = [ { domain = "jalr.de"; @@ -26,13 +24,18 @@ messageSizeLimit = 50 * 1024 * 1024; }; }; - services.postfix.config = { - smtp_bind_address = "159.69.103.126"; - smtp_bind_address_enforce = true; + services.postfix = { + config = { + smtp_bind_address = "159.69.103.126"; + smtp_bind_address_enforce = true; + }; + masterConfig.smtp.args = [ + "-o" + "inet_protocols=ipv4" + ]; }; - - security.acme.certs."hha.jalr.de" = { - dnsProvider = "hetzner"; - credentialsFile = pkgs.writeText "certbotCredentialsFile" "HETZNER_API_KEY_FILE=${config.sops.secrets.hetzner-api-key.path}"; + services.nginx.virtualHosts."hha.jalr.de" = { + enableACME = true; + forceSSL = true; }; } diff --git a/hosts/iron/services/matrix.nix b/hosts/iron/services/matrix.nix new file mode 100644 index 0000000..aa050d9 --- /dev/null +++ b/hosts/iron/services/matrix.nix @@ -0,0 +1,55 @@ +{ config, pkgs, ... }: + +let + inherit (config.networking) ports; + signalPhoneNumber = "+4915566437153"; + signalUser = "jalr"; +in +{ + sops.secrets.synapse-turn-shared-secret.owner = "matrix-synapse"; + jalr.matrix = { + enable = true; + fqdn = "matrix.jalr.de"; + domain = "jalr.de"; + synapse.port = ports.matrix-synapse.tcp; + turn = { + host = "turn.jalr.de"; + sharedSecretFile = config.sops.secrets.synapse-turn-shared-secret.path; + }; + mautrix-signal = { + enable = true; + port = ports.mautrix-signal.tcp; + settings.bridge = { + permissions = { + "@jalr:jalr.de" = "admin"; + "jalr.de" = "user"; + }; + default_bridge_presence = false; + send_presence_on_typing = false; + }; + }; + }; + + systemd.services.signal-cli-receive = { + description = "Run signal-cli to receive messages"; + serviceConfig = { + Type = "oneshot"; + User = signalUser; + CapabilityBoundingSet = null; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallFilter = "@system-service"; + }; + script = "${pkgs.signal-cli}/bin/signal-cli -u ${signalPhoneNumber} receive"; + }; + systemd.timers.signal-cli-receive = { + description = "Run signal-cli to receive messages"; + after = [ "network.target" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* *:00:00"; + Unit = config.systemd.services.signal-cli-receive.name; + }; + }; +} diff --git a/hosts/iron/services/matrix/default.nix b/hosts/iron/services/matrix/default.nix deleted file mode 100644 index f342253..0000000 --- a/hosts/iron/services/matrix/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - imports = [ - ./synapse.nix - ]; -} diff --git a/hosts/iron/services/matrix/synapse.nix b/hosts/iron/services/matrix/synapse.nix deleted file mode 100644 index 92357c9..0000000 --- a/hosts/iron/services/matrix/synapse.nix +++ /dev/null @@ -1,107 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.services.matrix-synapse.settings; - fqdn = "matrix.jalr.de"; - domain = "jalr.de"; - turnHost = "turn.jalr.de"; -in -{ - sops.secrets = { - synapse-turn-shared-secret = { - owner = "matrix-synapse"; - sopsFile = ../../secrets.yaml; - }; - }; - - services.matrix-synapse = { - enable = true; - - settings = { - server_name = domain; - public_baseurl = "https://${fqdn}"; - - database.name = "sqlite3"; - - listeners = lib.singleton { - port = 8008; - bind_addresses = [ "127.0.0.1" "::1" ]; - type = "http"; - tls = false; - x_forwarded = true; - resources = lib.singleton { - names = [ "client" "federation" "metrics" ]; - compress = false; - }; - }; - - turn_uris = [ - "turns:${turnHost}:5349?transport=udp" - "turns:${turnHost}:5349?transport=tcp" - "turn:${turnHost}:3478?transport=udp" - "turn:${turnHost}:3478?transport=tcp" - ]; - turn_user_lifetime = "1h"; - - enable_metrics = true; - - # adapted from https://github.com/NixOS/nixpkgs/blob/7e10bf4327491a6ebccbe1aaa8e6c6c0aca4663a/nixos/modules/services/misc/matrix-synapse-log_config.yaml - # - set root.level to WARNING instead of INFO - log_config = pkgs.writeText "log_config.yaml" (builtins.toJSON { - version = 1; - - formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; - - filters.context = { - "()" = "synapse.util.logcontext.LoggingContextFilter"; - request = ""; - }; - - handlers.journal = { - class = "systemd.journal.JournalHandler"; - formatter = "journal_fmt"; - filters = [ "context" ]; - SYSLOG_IDENTIFIER = "synapse"; - }; - - root = { - level = "WARNING"; - handlers = [ "journal" ]; - }; - - disable_existing_loggers = false; - }); - - max_upload_size = "50M"; - - # I’m okay with using matrix.org as trusted key server - suppress_key_server_warning = true; - - # For mautrix-whatsapp backfilling - experimental_features.msc2716_enabled = true; - }; - - extraConfigFiles = with config.sops.secrets; [ - synapse-turn-shared-secret.path - ]; - }; - - services.nginx.virtualHosts = { - "${fqdn}" = { - enableACME = true; - forceSSL = true; - - locations."/_matrix" = - let - listenerCfg = (lib.elemAt cfg.listeners 0); - in - { - proxyPass = "http://${lib.elemAt listenerCfg.bind_addresses 0}:${toString listenerCfg.port}"; - - extraConfig = '' - client_max_body_size ${cfg.max_upload_size}; - ''; - }; - }; - - }; -} diff --git a/hosts/iron/services/navidrome.nix b/hosts/iron/services/navidrome.nix index 4341818..d001dfb 100644 --- a/hosts/iron/services/navidrome.nix +++ b/hosts/iron/services/navidrome.nix @@ -1,11 +1,13 @@ -{ config, lib, pkgs, utils, ... }: +{ config, lib, pkgs, ... }: + let - port = 4533; + inherit (config.networking) ports; settings = { # https://www.navidrome.org/docs/usage/configuration-options/#available-options Address = "127.0.0.1"; - Port = port; + Port = ports.navidrome.tcp; DevActivityPanel = false; + MusicFolder = "/var/lib/navidrome/music"; }; passwordEncryptionKeyFile = config.sops.secrets.navidrome-password-encryption-key.path; configFile = (pkgs.formats.json { }).generate "navidrome.json" settings; @@ -16,21 +18,18 @@ let if [ -e "''$password_encryption_key_file" ]; then export ND_PASSWORDENCRYPTIONKEY="$(cat "''$password_encryption_key_file")" fi - exec ${pkgs.navidrome}/bin/navidrome --configfile ${configFile} + exec ${config.services.navidrome.package}/bin/navidrome --configfile ${configFile} ''; in { services.navidrome.enable = true; systemd.services.navidrome = { serviceConfig = { - ExecStart = lib.mkForce "${utils.systemdUtils.lib.makeJobScript "navidrome-start" script} %d"; + ExecStart = lib.mkForce "${pkgs.writeShellScript "navidrome-start" script} %d"; } // lib.attrsets.optionalAttrs (passwordEncryptionKeyFile != null) { LoadCredential = "PasswordEncryptionKey:${passwordEncryptionKeyFile}"; }; }; - sops.secrets.navidrome-password-encryption-key = { - sopsFile = ../secrets.yaml; - }; services.nginx.virtualHosts."navidrome.jalr.de" = { enableACME = true; forceSSL = true; @@ -38,7 +37,7 @@ in extraConfig = '' add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; location / { - proxy_pass http://127.0.0.1:${toString port}; + proxy_pass http://127.0.0.1:${toString ports.navidrome.tcp}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/hosts/iron/services/nginx.nix b/hosts/iron/services/nginx.nix index dd381d3..6eb78ee 100644 --- a/hosts/iron/services/nginx.nix +++ b/hosts/iron/services/nginx.nix @@ -1,7 +1,13 @@ -{ pkgs, ... }: +{ config, ... }: + +let + inherit (config.networking) ports; +in { services.nginx = { enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; recommendedGzipSettings = true; recommendedOptimisation = true; recommendedProxySettings = true; diff --git a/hosts/iron/services/ntp.nix b/hosts/iron/services/ntp.nix new file mode 100644 index 0000000..b10a245 --- /dev/null +++ b/hosts/iron/services/ntp.nix @@ -0,0 +1,12 @@ +{ + services.chrony = { + enable = true; + extraConfig = '' + allow 192.168.42.0/24 + allow 10.20.0.0/22 + leapsectz right/UTC + ''; + enableRTCTrimming = true; + }; + networking.firewall.allowedUDPPorts = [ 123 ]; +} diff --git a/hosts/iron/services/photoprism.nix b/hosts/iron/services/photoprism.nix new file mode 100644 index 0000000..b4dfb05 --- /dev/null +++ b/hosts/iron/services/photoprism.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: +let + domain = "media.weinturm-open-air.de"; + nextcloudDomain = "cloud.weinturm-open-air.de"; + inherit (config.networking) ports; + cfg = config.services.photoprism; + readSecretWrapper = pkgs.writeShellScriptBin "photoprism" '' + export PHOTOPRISM_OIDC_SECRET=$(cat "$CREDENTIALS_DIRECTORY/PHOTOPRISM_OIDC_SECRET_FILE") + + tagline[0]="Dein Blick. Unser Festival." + tagline[1]="Zeig uns das Festival durch deine Linse!" + tagline[2]="Gemeinsam festgehalten – Festivalmomente von euch für alle." + tagline[3]="Mach’s unvergesslich – lade deine Festivalfotos hoch!" + tagline[4]="Die besten Shots kommen von dir – teile sie hier." + tagline[5]="Jede Perspektive zählt – dein Foto, unser Highlight." + tagline[6]="Klick. Hochladen. Festivalgeschichte schreiben." + tagline[7]="Von der Crowd für die Crowd – Festivalfotos zum Verlieben." + tagline[8]="Dein Beitrag zum Festival-Archiv – jetzt Fotos teilen!" + tagline[9]="Weil kein Moment verloren gehen darf – deine Kamera zählt." + + size=''${#tagline[@]} + index=$(($RANDOM % $size)) + export PHOTOPRISM_SITE_CAPTION="''${tagline[$index]}" + + exec ${pkgs.photoprism}/bin/photoprism "$@" + ''; +in +{ + systemd.services.photoprism.serviceConfig.LoadCredential = lib.mkForce "PHOTOPRISM_OIDC_SECRET_FILE:${config.sops.secrets."photoprism/oidc-secret".path}"; + + services.photoprism = { + enable = true; + originalsPath = "/weinturm/photoprism"; + port = ports.photoprism.tcp; + package = readSecretWrapper; + settings = { + PHOTOPRISM_SITE_URL = "https://${domain}/"; + PHOTOPRISM_OIDC_URI = "https://${nextcloudDomain}"; + PHOTOPRISM_OIDC_CLIENT = "WnqjmaPJ5c0dY7KaWmvXVVgJYNjztqTKBZ6Wq6bjYXGOwM2Xuzx2WabFlnJVRCSE"; # Client ID from settings + PHOTOPRISM_OIDC_SCOPES = "openid profile email roles"; + PHOTOPRISM_OIDC_PROVIDER = "Nextcloud"; + PHOTOPRISM_OIDC_ICON = "https://${nextcloudDomain}/apps/theming/image/logo"; + PHOTOPRISM_OIDC_REDIRECT = "true"; + PHOTOPRISM_OIDC_REGISTER = "true"; + PHOTOPRISM_OIDC_USERNAME = "preferred_username"; + PHOTOPRISM_OIDC_WEBDAV = "true"; + PHOTOPRISM_ORIGINALS_LIMIT = toString (20 * 1024); # maximum size of media files in MB + PHOTOPRISM_INDEX_SCHEDULE = "@every 4h"; + PHOTOPRISM_DEFAULT_LOCALE = "de"; + PHOTOPRISM_DEFAULT_TIMEZONE = "Europe/Berlin"; + PHOTOPRISM_SITE_TITLE = "Weinturm Medien"; + PHOTOPRISM_SITE_CAPTION = ""; + PHOTOPRISM_SITE_AUTHOR = "Jugend- und Kultur Förderverein e.V."; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + proxyWebsockets = true; + }; + }; + }; +} diff --git a/hosts/iron/services/prometheus.nix b/hosts/iron/services/prometheus.nix new file mode 100644 index 0000000..5d571bc --- /dev/null +++ b/hosts/iron/services/prometheus.nix @@ -0,0 +1,207 @@ +{ config +, lib +, pkgs +, ... +}: +let + #domain = ""; + cfg = config.services.prometheus; + mkStaticTargets = targets: lib.singleton { inherit targets; }; + inherit (config.networking) ports; + blackboxRelabelConfig = [ + { + source_labels = [ "__address__" ]; + target_label = "__param_target"; + } + { + source_labels = [ "__param_target" ]; + target_label = "instance"; + } + { + target_label = "__address__"; + replacement = with config.services.prometheus.exporters.blackbox; "${listenAddress}:${toString port}"; + } + ]; +in +{ + #sops.secrets.prometheus-htpasswd = { + # owner = "nginx"; + # sopsFile = ../secrets.yaml; + #}; + + services.prometheus = { + enable = true; + listenAddress = "127.0.0.1"; + #webExternalUrl = "https://${domain}"; + globalConfig = { + scrape_interval = "15s"; + evaluation_interval = "15s"; + }; + extraFlags = [ + "--storage.tsdb.retention.time=90d" + "--web.enable-admin-api" + ]; + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [ + { + targets = with config.services.prometheus.exporters.node; [ + "${listenAddress}:${toString port}" + ]; + } + ]; + relabel_configs = [ + { + source_labels = [ "__address__" ]; + target_label = "instance"; + replacement = config.networking.hostName; + } + ]; + } + { + job_name = "vodafone_station"; + static_configs = mkStaticTargets [ + "127.0.0.1:${toString ports.prometheus-vodafone-station-exporter.tcp}" + ]; + } + { + job_name = "unifi"; + static_configs = mkStaticTargets [ + "${cfg.exporters.unpoller.listenAddress}:${toString cfg.exporters.unpoller.port}" + ]; + } + { + job_name = "blackbox"; + metrics_path = "/probe"; + params.module = [ "http_2xx" ]; + static_configs = [ + { + targets = [ + "https://c58r0l3wtmqltl4y.myfritz.net:44919/" + ]; + } + ]; + relabel_configs = blackboxRelabelConfig; + } + { + job_name = "internet_ip4"; + static_configs = mkStaticTargets [ "1.1.1.1" "8.8.8.8" ]; + metrics_path = "/probe"; + params.module = [ "icmp_ip4" ]; + relabel_configs = blackboxRelabelConfig; + } + { + job_name = "internet_ip6"; + static_configs = mkStaticTargets [ "2606:4700:4700::1111" "2001:4860:4860::8888" ]; + metrics_path = "/probe"; + params.module = [ "icmp_ip6" ]; + relabel_configs = blackboxRelabelConfig; + } + ]; + + exporters = { + node.enable = true; + + blackbox = { + enable = true; + listenAddress = "127.0.0.1"; + + # https://github.com/prometheus/blackbox_exporter/blob/master/CONFIGURATION.md + configFile = pkgs.writeText "prometheus-blackbox-config" (builtins.toJSON { + modules = { + icmp_ip4 = { + prober = "icmp"; + timeout = "5s"; + icmp = { + ip_protocol_fallback = false; + preferred_ip_protocol = "ip4"; + }; + }; + icmp_ip6 = { + prober = "icmp"; + timeout = "5s"; + icmp = { + ip_protocol_fallback = false; + preferred_ip_protocol = "ip6"; + }; + }; + http_2xx = { + prober = "http"; + timeout = "5s"; + http = { + valid_http_versions = [ "HTTP/1.1" "HTTP/2.0" ]; + valid_status_codes = [ ]; # Defaults to 2xx + method = "GET"; + follow_redirects = true; + fail_if_ssl = false; + fail_if_not_ssl = true; + tls_config = { + insecure_skip_verify = false; + }; + preferred_ip_protocol = "ip4"; # defaults to "ip6" + ip_protocol_fallback = false; # no fallback to "ip6" + }; + }; + }; + }); + }; + }; + }; + + /* + */ + # + + systemd.services.prometheus-vodafone-station-exporter = + let + unitName = "prometheus-vodafone-station-exporter"; + in + { + enable = true; + description = "Prometheus Vodafone Station exporter"; + wants = [ "network.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + BindReadOnlyPaths = [ + "/nix/store" + "/etc/resolv.conf" + ]; + DynamicUser = "yes"; + ExecStart = lib.strings.concatStringsSep " " [ + "${pkgs.vodafone-station-exporter}/bin/vodafone-station-exporter" + "-web.listen-address" + "127.0.0.1:${toString ports.prometheus-vodafone-station-exporter.tcp}" + "-vodafone.station-url" + "http://192.168.100.1" + "-vodafone.station-password-file" + "\${CREDENTIALS_DIRECTORY}/password" + ]; + LoadCredential = "password:${config.sops.secrets."prometheus/exporters/vodafone-station".path}"; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + RootDirectory = "%t/${unitName}"; + RuntimeDirectory = [ unitName ]; + }; + }; + + /* + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + #basicAuthFile = config.sops.secrets.prometheus-htpasswd.path; + + locations = { + "/".proxyPass = "http://${cfg.listenAddress}:${toString cfg.port}"; + }; + }; + */ +} diff --git a/hosts/iron/services/public-ip-tunnel.nix b/hosts/iron/services/public-ip-tunnel.nix index 5a0cce7..d6b4fcc 100644 --- a/hosts/iron/services/public-ip-tunnel.nix +++ b/hosts/iron/services/public-ip-tunnel.nix @@ -1,7 +1,8 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: let - listenPort = 51000; + inherit (config.networking) ports; + listenPort = ports.wireguard-public-ip-tunnel.udp; remoteHost = "magnesium.jalr.de"; remotePort = 51000; publicKey = "ABZCQfzlHJ1/iNbWFf6jVvdqSmqjxm3w5bpa0SYclBU="; @@ -12,46 +13,38 @@ let }; in { - sops.secrets = ( - lib.listToAttrs (map - (name: lib.nameValuePair "wireguard_key_${name}" { - sopsFile = ../secrets.yaml; - }) - [ - "hetzner-ha" - ] - ) - ); - - networking.iproute2.enable = true; - networking.iproute2.rttablesExtraConfig = '' - ${toString rtTable.id} ${rtTable.name} - ''; - - networking.wireguard.interfaces = { - hetzner-ha = { - ips = [ "${externalIp}/32" ]; - privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; - listenPort = listenPort; - table = rtTable.name; - postSetup = '' - ${pkgs.iproute2}/bin/ip rule add from ${externalIp} to 192.168.0.0/16 table main priority 10 - ${pkgs.iproute2}/bin/ip rule add from ${externalIp} table ${rtTable.name} priority 20 + networking = { + iproute2 = { + enable = true; + rttablesExtraConfig = '' + ${toString rtTable.id} ${rtTable.name} ''; - postShutdown = '' - ${pkgs.iproute2}/bin/ip rule del from ${externalIp} to 192.168.0.0/16 table main priority 10 - ${pkgs.iproute2}/bin/ip rule del from ${externalIp} table ${rtTable.name} priority 20 - ''; - peers = [{ - publicKey = publicKey; - endpoint = "${remoteHost}:${toString remotePort}"; - persistentKeepalive = 25; - allowedIPs = [ - "0.0.0.0/0" - ]; - }]; }; + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces.hetzner-ha = + let + addRule = rule: "ip rule add " + rule; + deleteRule = rule: "ip rule delete " + rule; + rules = [ + "from ${externalIp} to 192.168.0.0/16 table main priority 10" + "from ${externalIp} table ${rtTable.name} priority 20" + ]; + in + { + ips = [ "${externalIp}/32" ]; + privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; + inherit listenPort; + table = rtTable.name; + postSetup = lib.concatLines (map addRule rules); + postShutdown = lib.concatLines (map deleteRule rules); + peers = [{ + inherit publicKey; + endpoint = "${remoteHost}:${toString remotePort}"; + persistentKeepalive = 25; + allowedIPs = [ + "0.0.0.0/0" + ]; + }]; + }; }; - - networking.firewall.allowedUDPPorts = [ listenPort ]; } diff --git a/hosts/iron/services/radicale.nix b/hosts/iron/services/radicale.nix index a4c68e3..d6cb95d 100644 --- a/hosts/iron/services/radicale.nix +++ b/hosts/iron/services/radicale.nix @@ -1,25 +1,30 @@ { config, ... }: + +let + inherit (config.networking) ports; +in { - sops.secrets.radicale-htpasswd = { - owner = "nginx"; - sopsFile = ../secrets.yaml; - }; + sops.secrets.radicale-htpasswd.owner = "nginx"; services.nginx.virtualHosts = { "cal.jalr.de" = { enableACME = true; forceSSL = true; basicAuthFile = config.sops.secrets.radicale-htpasswd.path; - locations."/radicale/" = { - proxyPass = "http://localhost:5232/"; - recommendedProxySettings = true; - #basicAuthFile = ""; - extraConfig = '' - proxy_set_header X-Script-Name /radicale; - proxy_set_header X-Remote-User $remote_user; - ''; - # proxy_pass_request_headers = on; - # underscores_in_headers = on; + locations = { + "/radicale/" = { + proxyPass = "http://127.0.0.1:${toString ports.radicale.tcp}/"; + recommendedProxySettings = true; + #basicAuthFile = ""; + extraConfig = '' + proxy_set_header X-Script-Name /radicale; + proxy_set_header X-Remote-User $remote_user; + ''; + # proxy_pass_request_headers = on; + # underscores_in_headers = on; + }; + "/.well-known/caldav".return = "301 $scheme://$host:$server_port/radicale"; + "/.well-known/carddav".return = "301 $scheme://$host:$server_port/radicale"; }; }; }; @@ -28,7 +33,7 @@ enable = true; settings = { server = { - hosts = "127.0.0.1:5232,[::1]:5232"; + hosts = "127.0.0.1:${toString ports.radicale.tcp},[::1]:${toString ports.radicale.tcp}"; ssl = false; }; encoding = { diff --git a/hosts/iron/services/remarkable.nix b/hosts/iron/services/remarkable.nix new file mode 100644 index 0000000..dc38c83 --- /dev/null +++ b/hosts/iron/services/remarkable.nix @@ -0,0 +1,45 @@ +{ lib, config, pkgs, ... }: +let + inherit (config.networking) ports; + domain = "rmfakecloud.jalr.de"; + cfg = config.services.rmfakecloud; + mkEnvironment = settings: lib.strings.concatLines ( + lib.attrsets.mapAttrsToList (name: value: "export ${name}='${value}'") settings + ); + managementScript = pkgs.writeShellScriptBin "rmfakecloud" '' + [[ $(id -u) == "rmfakecloud" ]] || exec sudo -u rmfakecloud -- "$0" "$@" + set -a + source "${config.sops.secrets.rmfakecloud.path}" + set +a + ${mkEnvironment cfg.extraSettings} + ''; +in +{ + sops.secrets.rmfakecloud = { + owner = "root"; + group = "root"; + mode = "0400"; + }; + services.rmfakecloud = { + enable = true; + storageUrl = "https://${domain}"; + port = ports.rmfakecloud.tcp; + # see https://ddvk.github.io/rmfakecloud/install/configuration/ + environmentFile = config.sops.secrets.rmfakecloud.path; + extraSettings = { + RM_TRUST_PROXY = "true"; + DATADIR = "/var/lib/rmfakecloud"; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}/"; + recommendedProxySettings = true; + }; + }; + + environment.systemPackages = [ managementScript ]; +} diff --git a/hosts/iron/services/snapcast/bluetooth-sink.nix b/hosts/iron/services/snapcast/bluetooth-sink.nix new file mode 100644 index 0000000..43db142 --- /dev/null +++ b/hosts/iron/services/snapcast/bluetooth-sink.nix @@ -0,0 +1,79 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + #bluez-alsa + bluez + bluez-tools + pipewire + wireplumber + ]; + + hardware.bluetooth = { + enable = true; + settings = { + General = { + Enable = "Source,Sink,Media,Socket"; + Class = "0x00041C"; + # Pairing always on + AlwaysPairable = "true"; + # Don't disable discoverability after timeout + DiscoverableTimeout = "0"; + # Faster but uses more power + FastConnectable = "true"; + # Allow repairing of existing devices + JustWorksRepairing = "always"; + # to show battery state + Experimental = true; + }; + }; + }; + + services = { + blueman.enable = true; + ofono.enable = true; + upower.enable = true; + }; + + systemd.services = { + bluetooth-auto-pair = { + wantedBy = [ "bluetooth.service" ]; + after = [ "bluetooth.service" ]; + bindsTo = [ "bluetooth.service" ]; + serviceConfig = { + Type = "simple"; + ExecStart = pkgs.writeShellScript "exec-start" '' + ${pkgs.bluez}/bin/bluetoothctl < /dev/null; do + sleep 1 + done + + echo "Sleeping..." + sleep 5m + + echo "Shutting down Android" + adb -s "$host:$port" shell -- reboot -p + ''; + }; + }; + systemd.timers."whatsapp-${vmName}" = { + description = "Start Android VM to run WhatsApp"; + after = [ "network.target" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 2:00:00"; + Unit = "whatsapp@${vmName}.service"; + }; + }; +} diff --git a/hosts/iron/services/wireguard-esphome.nix b/hosts/iron/services/wireguard-esphome.nix new file mode 100644 index 0000000..db8fca9 --- /dev/null +++ b/hosts/iron/services/wireguard-esphome.nix @@ -0,0 +1,21 @@ +{ config, ... }: + +let + inherit (config.networking) ports; + listenPort = ports.wireguard-esphome.udp; +in +{ + networking = { + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces.esphome = { + ips = [ "10.20.16.1/30" ]; + privateKeyFile = config.sops.secrets."wireguard_key/esphome".path; + inherit listenPort; + peers = [{ + publicKey = "WM5qDvEF+tInUhzzF5msUwBy8WRyQOuxYX7roNcqNRc="; + persistentKeepalive = 120; + allowedIPs = [ "10.20.16.2" ]; + }]; + }; + }; +} diff --git a/hosts/jalr-t520/configuration.nix b/hosts/jalr-t520/configuration.nix index b483412..e10957b 100644 --- a/hosts/jalr-t520/configuration.nix +++ b/hosts/jalr-t520/configuration.nix @@ -1,91 +1,42 @@ -{ config, pkgs, ... }: +{ lib, pkgs, ... }: { imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ./disko.nix + ../../users/jalr ]; networking = { hostName = "jalr-t520"; - networkmanager.enable = true; useDHCP = false; }; - # List packages installed in system profile. To search, run: - # $ nix search wget - environment.systemPackages = with pkgs; [ - gnome3.adwaita-icon-theme - ]; - - environment.variables.EDITOR = "nvim"; - - programs.mtr.enable = true; - programs.wireshark.enable = true; - - hardware.bluetooth.enable = true; - - hardware.sane.enable = true; - - services.blueman.enable = true; - - services.udisks2.enable = true; - - services.avahi.enable = true; - services.avahi.nssmdns = true; - - services.udev.extraRules = '' - # 16C0:0442 - Axoloti Core - SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0442", MODE:="0666" - # 0483:df11 - Axoloti Core (STM32 microcontroller) in DFU mode - SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE:="0666" - - # Samsung A5 - SUBSYSTEM=="usb", ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="6860", GROUP="dialout", MODE="0660" - ''; - jalr = { - bootloader = "grub2"; - gui.enable = true; + bootloader = "systemd-boot"; + bluetooth.enable = true; + gui = { + enable = true; + sway.enable = true; + gnome.enable = true; + }; workstation.enable = true; - sdr.enable = true; - libvirt.enable = true; - autologin.enable = true; - autologin.username = "jalr"; }; - networking.wg-quick.interfaces.wgkalle = { - address = [ - "172.16.254.5/24" - "fd00::604:0:0:ac10:fe05/96" - ]; - privateKeyFile = "/root/wireguard-keys/wgkalle"; - listenPort = 51820; - mtu = 1296; - - peers = [ - { - publicKey = "52kAcBdnrFeSuVupHs0u4diUf6tpF8Esy4vzJAlT5Tc="; - endpoint = "78.47.224.233:1194"; - #endpoint = "[2a01:4f8:190:6068::2]:1194"; - persistentKeepalive = 60; - allowedIPs = [ - "0.0.0.0/0" - "::/0" - ]; - } - ]; + hardware = { + cpu.intel.updateMicrocode = true; + firmware = [ pkgs.linux-firmware ]; }; - networking.firewall.allowedUDPPorts = [ - 51820 # wireguard + + powerManagement.cpuFreqGovernor = "performance"; + + environment.systemPackages = with pkgs; [ + intel-media-driver + libva + libva-utils + libva1 ]; - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + hardware.graphics.extraPackages = lib.singleton pkgs.vaapiIntel; + system.stateVersion = "25.05"; } diff --git a/hosts/jalr-t520/disko.nix b/hosts/jalr-t520/disko.nix new file mode 100644 index 0000000..e75beca --- /dev/null +++ b/hosts/jalr-t520/disko.nix @@ -0,0 +1,65 @@ +{ config, ... }: +let + cfg = config.disko; +in +{ + disko.devices = { + disk = { + system = { + type = "disk"; + device = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_120GB_S21UNSAG200527E"; + content = { + type = "gpt"; + partitions = { + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + nixos = { + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/home/.snapshots" = { + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "noatime" "nodev" ]; + }; + }; + postCreateHook = + let + inherit (cfg.devices.disk.system.content.partitions.nixos) device; + in + '' + mountpoint="$(mktemp -d)" + mount "${device}" "$mountpoint" -o subvol=/ + trap 'umount "$mountpoint"; rmdir "$mountpoint"' EXIT + btrfs subvolume snapshot -r $mountpoint/root $mountpoint/root-blank + ''; + }; + }; + }; + }; + }; + }; + }; +} + + diff --git a/hosts/jalr-t520/hardware-configuration.nix b/hosts/jalr-t520/hardware-configuration.nix deleted file mode 100644 index b744c59..0000000 --- a/hosts/jalr-t520/hardware-configuration.nix +++ /dev/null @@ -1,60 +0,0 @@ -{ lib, pkgs, modulesPath, ... }: - -{ - imports = [ - "${modulesPath}/installer/scan/not-detected.nix" - ]; - - boot = { - initrd = { - availableKernelModules = [ - "aes_generic" - "aesni_intel" - "ahci" - "cryptd" - "ehci_pci" - "firewire_ohci" - "i915" - "sd_mod" - "sdhci_pci" - "usb_storage" - "usbhid" - ]; - kernelModules = [ "dm-snapshot" ]; - luks.devices = { - pvcrypt = { - device = "/dev/disk/by-uuid/3a1a8fec-b028-45c0-8432-7fcbe4615f44"; - preLVM = true; - }; - }; - }; - kernelModules = [ "kvm-intel" ]; - }; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/62e31097-dc6e-4f91-a043-1a6b8a154201"; - fsType = "ext4"; - }; - "/boot" = { - device = "/dev/disk/by-uuid/c4df83d7-8985-47df-b5cd-bf18bd490a50"; - fsType = "ext2"; - }; - }; - - boot.loader.grub.devices = [ "/dev/disk/by-id/ata-INTEL_SSDSC2BB016T4_BTWD531101JM1P6HGN" ]; - - nix.settings.max-jobs = 4; - - hardware.cpu.intel.updateMicrocode = true; - - powerManagement.cpuFreqGovernor = "performance"; - - environment.systemPackages = with pkgs; [ - intel-media-driver - libva - libva-utils - libva1 - ]; - hardware.opengl.extraPackages = lib.singleton pkgs.vaapiIntel; -} diff --git a/hosts/magnesium/configuration.nix b/hosts/magnesium/configuration.nix index 04e23d4..626f0b8 100644 --- a/hosts/magnesium/configuration.nix +++ b/hosts/magnesium/configuration.nix @@ -1,60 +1,32 @@ -{ config, lib, pkgs, ... }: +{ lib, ... }: { imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ../../modules/providers/hetzner-cloud.nix ./services + ../../users/jalr + ./persistence.nix + ./ports.nix ]; networking.hostName = "magnesium"; - services.openssh.enable = true; + + disko.devices.disk.virt.device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_60640534"; + security.sudo.wheelNeedsPassword = false; - networking.useDHCP = false; + systemd.network.networks."10-wan".address = [ + "2a01:4f8:c013:bab7::1/64" + ]; - systemd.network = { + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + zramSwap = { enable = true; - networks."10-wan" = { - matchConfig.Name = "enp1s0"; - networkConfig.DHCP = "no"; - address = [ - "162.55.35.199/32" - "2a01:4f8:c012:21ba::/64" - ]; - routes = [ - { - routeConfig.Destination = "172.31.1.1"; - } - { - routeConfig = { - Gateway = "172.31.1.1"; - GatewayOnLink = true; - }; - } - { - routeConfig.Gateway = "fe80::1"; - } - ]; - }; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; }; - # Use the systemd-boot EFI boot loader. - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - jalr = { - bootloader = "systemd-boot"; - uefi.enable = true; - }; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It's perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? - + system.stateVersion = "24.11"; } - diff --git a/hosts/magnesium/hardware-configuration.nix b/hosts/magnesium/hardware-configuration.nix deleted file mode 100644 index b4eba9e..0000000 --- a/hosts/magnesium/hardware-configuration.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ - (modulesPath + "/profiles/qemu-guest.nix") - ]; - - boot.initrd.availableKernelModules = [ "xhci_pci" "virtio_pci" "virtio_scsi" "usbhid" "sr_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = - { - device = "/dev/disk/by-uuid/45dcac99-1f65-48ab-b5bf-8a1507f0b75a"; - fsType = "btrfs"; - options = [ - "subvol=root" - "compress=zstd" - ]; - }; - - fileSystems."/home" = - { - device = "/dev/disk/by-uuid/45dcac99-1f65-48ab-b5bf-8a1507f0b75a"; - fsType = "btrfs"; - options = [ - "subvol=home" - "compress=zstd" - ]; - }; - - fileSystems."/nix" = - { - device = "/dev/disk/by-uuid/45dcac99-1f65-48ab-b5bf-8a1507f0b75a"; - fsType = "btrfs"; - options = [ - "subvol=nix" - "compress=zstd" - "noatime" - ]; - }; - - fileSystems."/boot" = - { - device = "/dev/disk/by-uuid/7836-0C48"; - fsType = "vfat"; - }; - - swapDevices = [ ]; - - nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; -} diff --git a/hosts/magnesium/persistence.nix b/hosts/magnesium/persistence.nix new file mode 100644 index 0000000..9d8ef4a --- /dev/null +++ b/hosts/magnesium/persistence.nix @@ -0,0 +1,51 @@ +{ config, lib, ... }: +{ + boot.initrd.postDeviceCommands = + let + inherit (config.disko.devices.disk.virt.content.partitions.linux) device; + in + lib.mkAfter '' + mkdir /mnt + mount -t btrfs "${device}" /mnt + btrfs subvolume list -o /mnt/root | cut -f9 -d' ' | while read subvolume; do + btrfs subvolume delete "/mnt/$subvolume" + done + btrfs subvolume delete /mnt/root + btrfs subvolume snapshot /mnt/root-blank /mnt/root + ''; + + services = { + openssh = { + hostKeys = lib.mkForce [{ + path = "/persist/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + }]; + }; + forgejo.stateDir = "/persist/var/lib/forgejo"; + postgresql.dataDir = "/persist/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}"; + }; + + fileSystems."/persist".neededForBoot = true; + environment.persistence."/persist" = { + hideMounts = true; + directories = [ + "/var/lib/acme" + "/var/lib/hedgedoc" + "/var/lib/nixos" + "/var/lib/private/mealie" + "/var/lib/private/ntfy-sh" + { + directory = "/var/lib/private/tandoor-recipes"; + user = "tandoor_recipes"; + group = "tandoor_recipes"; + mode = "u=rwx,g=rx,o="; + } + { + directory = "/var/lib/trilium"; + user = "trilium"; + group = "trilium"; + mode = "u=rwx,g=rx,o="; + } + ]; + }; +} diff --git a/hosts/magnesium/ports.nix b/hosts/magnesium/ports.nix new file mode 100644 index 0000000..fd74f41 --- /dev/null +++ b/hosts/magnesium/ports.nix @@ -0,0 +1,19 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + coturn-cli.tcp = 5766; + coturn-plain = { tcp = [ 3478 3479 ]; udp = [ 3478 3479 ]; }; + coturn-relay.udp = { from = 49160; to = 49200; }; + coturn-tls = { tcp = [ 5349 5350 ]; udp = [ 5349 5350 ]; }; + forgejo-ssh.tcp = 2022; + hedgedoc.tcp = 3000; + mealie.tcp = 9000; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + ntfy.tcp = 12474; + tandoor.tcp = 9001; + trilium.tcp = 12783; + wireguard-public-ip-tunnel.udp = 51000; + }; +} diff --git a/hosts/magnesium/secrets.yaml b/hosts/magnesium/secrets.yaml index fc00ca5..92851b1 100644 --- a/hosts/magnesium/secrets.yaml +++ b/hosts/magnesium/secrets.yaml @@ -1,33 +1,39 @@ wireguard_key_hetzner-ha: ENC[AES256_GCM,data:HEW+EalHg6/mq7pRKZkasGz0nqbkSppkf0H/uV5QMJnWwKw9a9W21Y77OSw=,iv:OA6yml1T5kVafX0RYd0Es7DHcGjJazUxP2M6a5Pwkag=,tag:lX5UPIseIQ136HLrHbzZyw==,type:str] turn-static-auth-secret: ENC[AES256_GCM,data:rzhixUemFPwKj1BcVPZd7KtUO9OA6A2R4qEQ1BZGVG0=,iv:uYHYe4Cywxovt3b/Ho1tQVHrpgVic+AKh9AjYMYSZcM=,tag:rr8RW/if06t38GpZCYQB4w==,type:str] +gitlab-runner_fablab-nea-hcloud-labsync: ENC[AES256_GCM,data:+znVO8cQxjDdhch7oUALZvt84iJmWnAx6lTM0+WGkGtaRWTCTPjgnst5waSJpw/Oysrd1PkXZKmLHyHuU7K/CHQij7sWH50G3ZcUum58klJc3dCPztlrLpDVHeSwyYiLpsqkQTfjqLPfrMkxuxBgTEVXlq2ZnFuyOGbFx9hubPxLeyQKakiW3qZWGjbFXYAps7Gl61AVdKJj3y1otX2JbCjG9x2i6FHZpl5ywwQCjKNM,iv:7v+I/oJtWDap6PNIJ4Qm3Si9dGs7a79SaMhnr/tbe1A=,tag:7jgoLtdWAEKMkWoXZ10owA==,type:str] +forgejo-mail: ENC[AES256_GCM,data:eZv9dM0a06wFJaDUZjo=,iv:L32ab5k/AX8HqSACJA5w+WbzLlBijA5++Gcr2SrnYIU=,tag:ddyTXikWTMnxq86IijgyYg==,type:str] +hedgedoc-session-secret: ENC[AES256_GCM,data:AYUiUF7R+5C3F5kNRL0R95e1l3Y59tIP388uY0IYCskBhR0H0XMVvyrX/gIM33Twwkc5it+fQtNPNXsbrAnoKQ==,iv:Q6pDEdFplp845/DCHutwni/g7Ch39pTCvfNs4Eh28CQ=,tag:aqVGs3iThmepT7iJusLOMA==,type:str] +mealie: ENC[AES256_GCM,data:4LlxJjDstTPZCD7Xyb+0CRkeDafP9a9oMuYDnXznINe+LrfkJGKwQIwP0B3VpeMmZ0Rwe7Tvje0ZWySFGADireb2r7TjDyASAoXJDyNNJ8byRc5Zt77zL2dp/W4xVt8WpQvwsXosjDv3NN6we831wWUrfNtp0g34YLqSU3F/9i7AaU7nVKnQ9QtJRVg5O57nhs/ZXopKOBUdiKAmxcl0hNNdQdaQX6xkDCWrV4432IOckqyqEQyd9KeCURuWeTUgPmTmnt9Cj8KkaQ39fd0LAGRjOBsKo4C4,iv:o5BPW4Wcg4KcFkJHc/mdrO4Rh+1nifxulYkF+iM3LEw=,tag:KXwDr3VHxjeHkyo23SPJgA==,type:str] +tandoor: + secret_key: ENC[AES256_GCM,data:8aVuOBljF+vnEXOzi0r2xUtUGlZM50MuBXK70XW78Q9jNq4ZuRciGabnYwFfknb2/tA=,iv:KN7DwcMH5NN5BgWFgO/V1dfSyiHfAM2wS86atYcBdlQ=,tag:wQA7QL8VqP5d4uBSNQLsnQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - - recipient: age1swv42gad884z2v75kateem6k2za6ltkq6wu90ewqp6dp7gxprawslwz0w0 + - recipient: age19qkgfaq08kmyxghet48dq4gxwjuy9zpvuyxys9jkmcqa5634537qlxjcd8 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwa0ZIdE9lc2lNZlN0UFBU - RWdxQm1oR01GemJOSE9ZU1RYc3crRGg5REF3ClUzaEhyelZNTVUxeEwvc1V3eDBt - SUx0UXU0aTdnTGlTaWJvd2R6ajZmNVkKLS0tICszejE3WVNOTHR6Rms2bjQrbzEz - Vlk3Y1luTTg3bkpqNTNPUGlNYmNtMW8K9dEUwAuzvDZZoVi8FPZQ7/h75EV0L+VM - MlTGfEt38Hi7EOw+yfXvXYHse/OKypwcrPiJDT6IT/E+O9BJCjPKCA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHMW5lbkRNQS9Gc0VUWndk + VlJiczFVM1dHUGc2QWZnMHVIVGZzdWJKUUJzCngyNGxaR2JFNG9HbG81c1ZNSlQ3 + MlgvMlNYWVduY1diM3g2U3BiL0J3U2sKLS0tIHBscWxMTzVISkE5WW1CZTNYK1ZM + elNwdVlJS2NCWUlXcEZvZWsvZ29FRnMK/qa6Qj1yQc91PWk9tMKSyFkMfYcHIKpQ + jcPmGWbpi2NPL/F0Xz2X/zQQxWzs9uzlS1VH+y8JRe1EPMYJ78NXZw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-07-10T19:12:04Z" - mac: ENC[AES256_GCM,data:cDwrW1odloAedY7tdKLPg52UTehlTrs3+lAH0ksaGGDXzQCsVNlfzR86SRGQY2s98cu7+9j5azhWSU9slDZcTIk4VWL2i8ZtVpD8KFtut0WiwWaGf2/KLe80GGw3lr4Rm491YDvv7JcUsEuCG3lAQFZzAlZcfl0faFpzYvpTk30=,iv:yeyRjURArUaG0HzcVP0Wm9n0oVHb+u4zNdaQbrC+EaM=,tag:9uFNd3CSSFjToeawBtMNHg==,type:str] + lastmodified: "2025-04-17T23:26:44Z" + mac: ENC[AES256_GCM,data:Dl4/6wrIwOsCRK979O9lSKyi4LKAG0CfgTGS3RwNu23MvhhaBNru4P1gPWWu7/YC6ad63Ip/RuVB69A1kUmgrYimZcU6E3iPg7vsqskmTU0caMD54CHemj57EYS7r8tcloBEgkOvM6Vn/Bs1dV1/EKAv9Kr6r4x6xb3UOofDcwM=,iv:pzRSKp3EnUpgMdwLDKrExpEkm+uZbU6/pYkVLbcnjrY=,tag:Z6DIPVcNUa8QihV1lsmUMA==,type:str] pgp: - - created_at: "2023-06-22T12:44:23Z" - enc: | + - created_at: "2025-04-08T22:53:53Z" + enc: |- -----BEGIN PGP MESSAGE----- - hF4D3ylLYNOsO+0SAQdAD/wwGspjkzL/xlqVxl8pixtRQGAlyuEJdTwja6e4bkAw - I+xwPhJH9FpkwArRKErtW9u6e9lM8zJOvgteseTRmQFkQ9fyTtXAx2lLg5JOFdYn - 0l4BkaozbVKjx1XEJBoBUF1YMfREKyrORk/kU2UTluQKkEp7xaojZkuhWEqEMC7N - tKVpPhef7M5escwcpQCpoI5+DCepJQDfoxyiAWx8P0a6tbV2F+X9y6kgb6iuWpf2 - =WNKv + hF4DY/xpNY5WhB0SAQdAbrDTh/Nvu8ky1ec34AAkKQcTH1G1nDlUCSfobMQsCmAw + XPI7V41rBAY2m6J1P/0oy9cHVfE/LUi4E/yCgNG7YIGdUbb9x29x7A3uoP1NAhE5 + 0l4BZQGZ+GGa69KZ2mOnWhbKfjtOVNDoaxcpgNWHxrtO35c/tNSCxJ2Uj2Q2u3Nj + +SRaHB3tsF8VL85Tn0FEXSWLzL7SfHj78wvaZ/3AxbqdF7WDJkl1hXEnrf2DjBCC + =Gi/Y -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.9.4 diff --git a/hosts/magnesium/services/coturn.nix b/hosts/magnesium/services/coturn.nix index 7a12474..bfd6e4a 100644 --- a/hosts/magnesium/services/coturn.nix +++ b/hosts/magnesium/services/coturn.nix @@ -1,16 +1,28 @@ { config, lib, pkgs, ... }: + let cfg = config.services.coturn; - fqdn = "turn.jalr.de"; + inherit (config.networking) ports; in { - sops.secrets.turn-static-auth-secret = { - owner = "turnserver"; - sopsFile = ../secrets.yaml; - }; + sops.secrets.turn-static-auth-secret.owner = "turnserver"; - services.coturn = { + services.coturn = ( + if ports.coturn-plain.tcp != ports.coturn-plain.udp then builtins.abort "coturn: plain TCP and UDP ports must match." + else if ports.coturn-tls.tcp != ports.coturn-tls.udp then builtins.abort "coturn: TLS TCP and UDP ports must match." + else if lib.lists.length ports.coturn-plain.tcp != 2 then builtins.abort "coturn: exactly two plain ports must be given." + else if lib.lists.length ports.coturn-tls.tcp != 2 then builtins.abort "coturn: exactly two TLS ports must be given." + else { + listening-port = builtins.elemAt ports.coturn-plain.tcp 0; + alt-listening-port = builtins.elemAt ports.coturn-plain.tcp 1; + tls-listening-port = builtins.elemAt ports.coturn-tls.tcp 0; + alt-tls-listening-port = builtins.elemAt ports.coturn-tls.tcp 1; + cli-port = ports.coturn-cli.tcp; + min-port = ports.coturn-relay.udp.from; + max-port = ports.coturn-relay.udp.to; + } + ) // { enable = true; # config adapted from synapse’s turn howto: @@ -22,12 +34,6 @@ in no-tcp-relay = true; - cert = "/run/turnserver/fullchain.pem"; - pkey = "/run/turnserver/key.pem"; - - min-port = 49160; - max-port = 49200; - no-cli = true; extraConfig = '' @@ -68,9 +74,19 @@ in systemd.services.coturn = { after = [ "acme-finished-${fqdn}.target" ]; serviceConfig = { - ExecStartPre = lib.singleton "!${pkgs.writeShellScript "coturn-setup-tls" '' - cp ${config.security.acme.certs."${fqdn}".directory}/{fullchain,key}.pem /run/turnserver/ - chgrp turnserver /run/turnserver/{fullchain,key}.pem + Environment = [ + "CERT_FILE=%d/fullchain.pem" + "KEY_FILE=%d/key.pem" + ]; + LoadCredential = [ + "fullchain.pem:${config.security.acme.certs."${fqdn}".directory}/fullchain.pem" + "key.pem:${config.security.acme.certs."${fqdn}".directory}/key.pem" + ]; + ExecStartPre = lib.singleton "${pkgs.writeShellScript "coturn-setup-tls" '' + cat >> /run/coturn/turnserver.cfg << EOF + cert="$CERT_FILE"; + pkey="$KEY_FILE"; + EOF ''}"; }; }; @@ -87,12 +103,8 @@ in }; networking.firewall = { - allowedTCPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port ]; - allowedUDPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port ]; - - allowedUDPPortRanges = lib.singleton { - from = cfg.min-port; - to = cfg.max-port; - }; + allowedTCPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port alt-tls-listening-port ]; + allowedUDPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port alt-tls-listening-port ]; + allowedUDPPortRanges = lib.singleton ports.coturn-relay.udp; }; } diff --git a/hosts/magnesium/services/default.nix b/hosts/magnesium/services/default.nix index 4039a4d..c257730 100644 --- a/hosts/magnesium/services/default.nix +++ b/hosts/magnesium/services/default.nix @@ -1,8 +1,15 @@ { imports = [ ./coturn.nix - ./mosquitto.nix + ./forgejo.nix + ./gitlab-runner.nix + ./hedgedoc.nix + ./it-tools.nix + ./mealie.nix + ./ntfy.nix ./public-ip-tunnel.nix + ./tandoor.nix + ./trilium.nix ./webserver.nix ]; } diff --git a/hosts/magnesium/services/forgejo.nix b/hosts/magnesium/services/forgejo.nix new file mode 100644 index 0000000..4484e16 --- /dev/null +++ b/hosts/magnesium/services/forgejo.nix @@ -0,0 +1,69 @@ +{ config, ... }: +let + domain = "git.jalr.de"; + cfg = config.services.forgejo; + inherit (config.networking) ports; +in +{ + sops.secrets.forgejo-mail.owner = cfg.user; + services.forgejo = { + enable = true; + lfs.enable = true; + secrets.mailer.PASSWD = config.sops.secrets.forgejo-mail.path; + settings = { + DEFAULT.APP_NAME = "jalr's git"; + avatar.DISABLE_GRAVATAR = true; + mailer = { + ENABLED = true; + PROTOCOL = "smtps"; + SMTP_ADDR = "hha.jalr.de"; + FROM = "git@jalr.de"; + USER = "git@jalr.de"; + }; + server = { + DOMAIN = domain; + PROTOCOL = "http+unix"; + ROOT_URL = "https://${domain}/"; + + DISABLE_ROUTER_LOG = true; + OFFLINE_MODE = true; + + BUILTIN_SSH_SERVER_USER = "git"; + START_SSH_SERVER = true; + SSH_PORT = ports.forgejo-ssh.tcp; + SSH_SERVER_HOST_KEYS = "ssh/forgejo.ed25519"; + }; + service = { + DEFAULT_ALLOW_CREATE_ORGANIZATION = false; + DEFAULT_KEEP_EMAIL_PRIVATE = true; + ENABLE_NOTIFY_MAIL = false; + REGISTER_MANUAL_CONFIRM = true; + DISABLE_REGISTRATION = true; + }; + session = { + PROVIDER = "file"; + COOKIE_SECURE = true; + }; + log.level = "Warn"; + }; + dump = { + enable = true; + type = "tar.zst"; + }; + }; + + networking.firewall.allowedTCPPorts = [ cfg.settings.server.SSH_PORT ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://unix:/run/forgejo/forgejo.sock"; + }; + + extraConfig = '' + client_max_body_size 1G; + ''; + }; +} diff --git a/hosts/magnesium/services/gitlab-runner.nix b/hosts/magnesium/services/gitlab-runner.nix new file mode 100644 index 0000000..ad17690 --- /dev/null +++ b/hosts/magnesium/services/gitlab-runner.nix @@ -0,0 +1,36 @@ +{ config, pkgs, ... }: + +{ + services.gitlab-runner = { + enable = true; + extraPackages = [ + #(pkgs.writeShellScriptBin "docker-machine" '' + # exec ${pkgs.docker-machine-gitlab}/bin/docker-machine --debug "$@" + #'') + pkgs.docker-machine-gitlab + ]; + #settings.log_level = "debug"; + services."fablab-nea-hcloud-labsync" = { + description = "FabLab NEA Hetzner Cloud - labsync image builder"; + limit = 5; + executor = "docker+machine"; + authenticationTokenConfigFile = config.sops.secrets.gitlab-runner_fablab-nea-hcloud-labsync.path; + dockerImage = "quay.io/official-images/alpine:latest"; + dockerPrivileged = true; + registrationFlags = [ + "--docker-tlsverify" + "--machine-idle-nodes 0" + "--machine-idle-scale-factor 0.0" + "--machine-idle-count-min 0" + "--machine-idle-time 900" + "--machine-max-builds 100" + "--machine-machine-driver hetzner" + "--machine-machine-name gitlabrunner-%s" + ] ++ (map (o: "--machine-machine-options=" + o) [ + "hetzner-image=debian-12" + "hetzner-server-type=cx22" + "hetzner-server-location=nbg1" + ]); + }; + }; +} diff --git a/hosts/magnesium/services/hedgedoc.nix b/hosts/magnesium/services/hedgedoc.nix new file mode 100644 index 0000000..d9ee1b8 --- /dev/null +++ b/hosts/magnesium/services/hedgedoc.nix @@ -0,0 +1,50 @@ +{ config, ... }: + +let + domain = "pad.jalr.de"; + cfg = config.services.hedgedoc; + inherit (config.networking) ports; +in +{ + sops.secrets.hedgedoc-session-secret.owner = config.systemd.services.hedgedoc.serviceConfig.User; + services = { + hedgedoc = { + enable = true; + settings = + let + day = 24 * 60 * 60 * 1000; + in + { + inherit domain; + protocolUseSSL = true; + csp.enable = true; + port = ports.hedgedoc.tcp; + db = { + dialect = "postgres"; + host = "/run/postgresql"; + user = "hedgedoc"; + database = "hedgedoc"; + }; + allowEmailRegister = false; + sessionSecret = config.sops.secrets.hedgedoc-session-secret.path; + sessionLife = 90 * day; + }; + }; + postgresql = { + enable = true; + ensureDatabases = [ "hedgedoc" ]; + ensureUsers = [{ + name = "hedgedoc"; + ensureDBOwnership = true; + }]; + }; + nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://${cfg.settings.host}:${toString cfg.settings.port}"; + }; + }; + }; +} diff --git a/hosts/magnesium/services/it-tools.nix b/hosts/magnesium/services/it-tools.nix new file mode 100644 index 0000000..4999bf0 --- /dev/null +++ b/hosts/magnesium/services/it-tools.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: + +let + domain = "tools.jalr.de"; +in +{ + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + root = "${pkgs.it-tools}/lib"; + }; +} diff --git a/hosts/magnesium/services/mealie.nix b/hosts/magnesium/services/mealie.nix new file mode 100644 index 0000000..a60f80e --- /dev/null +++ b/hosts/magnesium/services/mealie.nix @@ -0,0 +1,33 @@ +{ config, ... }: + +let + domain = "mealie.jalr.de"; + cfg = config.services.mealie; +in +{ + services.mealie = { + enable = true; + credentialsFile = config.sops.secrets.mealie.path; + port = config.networking.ports.mealie.tcp; + listenAddress = "127.0.0.1"; + settings = { + BASE_URL = "https://${domain}"; + ALLOW_SIGNUP = "false"; + SMTP_HOST = "hha.jalr.de"; + SMTP_PORT = 465; + SMTP_FROM_NAME = "mealie"; + SMTP_AUTH_STRATEGY = "TLS"; + SMTP_FROM_EMAIL = "no-reply@jalr.de"; + SMTP_USER = "mealie@jalr.de"; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + }; + }; +} diff --git a/hosts/magnesium/services/mosquitto.nix b/hosts/magnesium/services/mosquitto.nix deleted file mode 100644 index 6cb7c38..0000000 --- a/hosts/magnesium/services/mosquitto.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ config, lib, pkgs, ... }: -let port = 1883; -in -{ - services.mosquitto = { - enable = true; - persistence = true; - listeners = [ - { - port = port; - settings = { - allow_anonymous = true; - }; - } - ]; - }; - networking.firewall.allowedTCPPorts = [ port ]; -} diff --git a/hosts/magnesium/services/ntfy.nix b/hosts/magnesium/services/ntfy.nix new file mode 100644 index 0000000..16f9129 --- /dev/null +++ b/hosts/magnesium/services/ntfy.nix @@ -0,0 +1,34 @@ +{ config, ... }: +let + cfg = config.services.ntfy-sh; + domain = "ntfy.jalr.de"; + datadir = "/var/lib/ntfy-sh"; + inherit (config.networking) ports; +in +{ + # ntfy access --auth-file /var/lib/private/ntfy-sh/user.db '*' 'up*' write-only + + services.ntfy-sh = { + enable = true; + settings = { + listen-http = "127.0.0.1:${toString ports.ntfy.tcp}"; + base-url = "https://${domain}"; + behind-proxy = true; + #web-root = "disable"; + #auth-default-access = "read-only"; + attachment-cache-dir = "${datadir}/attachments"; + auth-file = "${datadir}/user.db"; + cache-file = "${datadir}/cache-file.db"; + }; + }; + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://${cfg.settings.listen-http}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/magnesium/services/public-ip-tunnel.nix b/hosts/magnesium/services/public-ip-tunnel.nix index 0cd32b4..1dee818 100644 --- a/hosts/magnesium/services/public-ip-tunnel.nix +++ b/hosts/magnesium/services/public-ip-tunnel.nix @@ -1,44 +1,37 @@ -{ config, lib, pkgs, ... }: +{ config, ... }: let - listenPort = 51000; + listenPort = ports.wireguard-public-ip-tunnel.udp; publicKey = "GCmQs7upvDYFueEfqD2yJkkOZg3K7YaGluWWzdjsyTo="; + inherit (config.networking) ports; in { - sops.secrets = ( - lib.listToAttrs (map - (name: lib.nameValuePair "wireguard_key_${name}" { - sopsFile = ../secrets.yaml; - }) - [ - "hetzner-ha" - ] - ) - ); - #boot.kernel.sysctl = { # "net.ipv4.conf.all.forwarding" = 1; # "net.ipv4.conf.hetzner-ha.proxy_arp" = 1; # "net.ipv4.conf.enp1s0.proxy_arp" = 1; #}; - networking.interfaces.hetzner-ha.proxyARP = true; - networking.interfaces.enp1s0.proxyARP = true; - networking.wireguard.interfaces = { - hetzner-ha = { - ips = [ ]; - privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; - listenPort = listenPort; + networking = { + interfaces = { + hetzner-ha.proxyARP = true; + enp1s0.proxyARP = true; + }; + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces = { + hetzner-ha = { + ips = [ ]; + privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; + inherit listenPort; - peers = [{ - publicKey = publicKey; - persistentKeepalive = 25; - allowedIPs = [ - "159.69.103.126/32" - ]; - }]; + peers = [{ + inherit publicKey; + persistentKeepalive = 25; + allowedIPs = [ + "159.69.103.126/32" + ]; + }]; + }; }; }; - - networking.firewall.allowedUDPPorts = [ listenPort ]; } diff --git a/hosts/magnesium/services/tandoor.nix b/hosts/magnesium/services/tandoor.nix new file mode 100644 index 0000000..1127cd2 --- /dev/null +++ b/hosts/magnesium/services/tandoor.nix @@ -0,0 +1,47 @@ +{ config, ... }: + +let + domain = "tandoor.jalr.de"; + cfg = config.services.tandoor-recipes; + #recipesDirectory = "/var/lib/private/tandoor-recipes/recipes"; + inherit (config.networking) ports; +in +{ + services.tandoor-recipes = { + enable = true; + port = ports.tandoor.tcp; + extraConfig = { + GUNICORN_MEDIA = "1"; + }; + }; + + systemd.services.tandoor-recipes = { + serviceConfig = { + LoadCredential = [ + "secret_key:${config.sops.secrets."tandoor/secret_key".path}" + ]; + Environment = [ + "SECRET_KEY_FILE=%d/secret_key" + ]; + }; + }; + + #users.groups.tandoor-recipes.members = [ "nginx" ]; + # https://tandoor.jalr.de/media/recipes/c071286f-60b3-45e9-9ac5-f4bb99703c17_11.jpg + + #systemd.services.nginx.serviceConfig.BindReadOnlyPaths = [ recipesDirectory ]; + #users.groups.tandoor-recipes.members = [ "nginx" ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + proxyWebsockets = true; + }; + #"/media/recipes/".alias = recipesDirectory; + }; + }; +} diff --git a/hosts/magnesium/services/trilium.nix b/hosts/magnesium/services/trilium.nix new file mode 100644 index 0000000..9b7adbf --- /dev/null +++ b/hosts/magnesium/services/trilium.nix @@ -0,0 +1,23 @@ +{ config, pkgs, ... }: + +let + domain = "notes.jalr.de"; + inherit (config.networking) ports; +in +{ + services.trilium-server = { + enable = true; + package = pkgs.trilium-next-server; + host = "127.0.0.1"; + port = ports.trilium.tcp; + nginx = { + enable = true; + hostName = domain; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + }; +} diff --git a/hosts/magnesium/services/webserver.nix b/hosts/magnesium/services/webserver.nix index 41990cc..a30a098 100644 --- a/hosts/magnesium/services/webserver.nix +++ b/hosts/magnesium/services/webserver.nix @@ -1,17 +1,35 @@ { config, lib, pkgs, ... }: + let domain = "jalr.de"; matrixDomain = "matrix.jalr.de"; + inherit (config.networking) ports; in { - networking.firewall.allowedTCPPorts = [ 80 443 ]; + networking.firewall.allowedTCPPorts = [ ports.nginx-http.tcp ports.nginx-https.tcp ]; services.nginx = { enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + commonHttpConfig = '' + map $scheme $hsts_header { + https "max-age=31536000"; + } + add_header Strict-Transport-Security $hsts_header; + + add_header Referrer-Policy strict-origin; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + ''; virtualHosts = { "${domain}" = { enableACME = true; forceSSL = true; - + root = pkgs.jalr.contact; locations = let # workaround for nginx dropping parent headers @@ -26,7 +44,7 @@ in add_header Content-Type application/json; return 200 '${builtins.toJSON { "m.server" = "${matrixDomain}:443"; - }}'; + }}'; ''; "=/.well-known/matrix/client".extraConfig = '' ${parentHeaders} @@ -34,6 +52,7 @@ in add_header Access-Control-Allow-Origin *; return 200 '${builtins.toJSON { "m.homeserver"."base_url" = "https://${matrixDomain}"; + "org.matrix.msc3575.proxy"."url" = "https://${matrixDomain}"; }}'; ''; }; diff --git a/hosts/weinturm-pretix-prod/configuration.nix b/hosts/weinturm-pretix-prod/configuration.nix deleted file mode 100644 index 9a8f0f2..0000000 --- a/hosts/weinturm-pretix-prod/configuration.nix +++ /dev/null @@ -1,55 +0,0 @@ -{ ... }: { - imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jalr.nix - ./services - ]; - - networking.hostName = "weinturm-pretix-prod"; - - networking.useDHCP = false; - - systemd.network = { - enable = true; - networks."10-wan" = { - matchConfig.Name = "enp1s0"; - networkConfig.DHCP = "no"; - address = [ - "142.132.185.70/32" - "2a01:4f8:c012:edd::/64" - ]; - routes = [ - { - routeConfig.Destination = "172.31.1.1"; - } - { - routeConfig = { - Gateway = "172.31.1.1"; - GatewayOnLink = true; - }; - } - { - routeConfig.Gateway = "fe80::1"; - } - ]; - }; - }; - - zramSwap = { - enable = true; - algorithm = "zstd"; - memoryPercent = 60; - priority = 1; - }; - - security.sudo.wheelNeedsPassword = false; - - services.netdata.enable = true; - - jalr = { - bootloader = "systemd-boot"; - uefi.enable = true; - }; - - system.stateVersion = "22.11"; -} diff --git a/hosts/weinturm-pretix-prod/hardware-configuration.nix b/hosts/weinturm-pretix-prod/hardware-configuration.nix deleted file mode 100644 index c96eb20..0000000 --- a/hosts/weinturm-pretix-prod/hardware-configuration.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ config, lib, pkgs, modulesPath, ... }: -{ - imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; - - boot.initrd.availableKernelModules = [ "xhci_pci" "virtio_pci" "virtio_scsi" "usbhid" "sr_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ ]; - boot.extraModulePackages = [ ]; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/766739e7-2c5c-4c28-b6ee-4bf9f91e6b1f"; - fsType = "btrfs"; - options = [ - "subvol=root" - "compress=zstd" - ]; - }; - "/home" = { - device = "/dev/disk/by-uuid/766739e7-2c5c-4c28-b6ee-4bf9f91e6b1f"; - fsType = "btrfs"; - options = [ - "subvol=home" - "compress=zstd" - ]; - }; - "/nix" = { - device = "/dev/disk/by-uuid/766739e7-2c5c-4c28-b6ee-4bf9f91e6b1f"; - fsType = "btrfs"; - options = [ - "subvol=nix" - "compress=zstd" - "noatime" - ]; - }; - "/boot" = { - device = "/dev/disk/by-uuid/A586-15AC"; - fsType = "vfat"; - }; - }; - - swapDevices = [ ]; - - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; -} diff --git a/hosts/weinturm-pretix-prod/secrets.yaml b/hosts/weinturm-pretix-prod/secrets.yaml deleted file mode 100644 index fe2b033..0000000 --- a/hosts/weinturm-pretix-prod/secrets.yaml +++ /dev/null @@ -1,33 +0,0 @@ -pretix-cfg: ENC[AES256_GCM,data:AgT82cee/BHR5V2JkNdDkbS82zXntOD6dLuEm4XUgal0Jpz+3ACqrEF72U0nNTd/JUYVD1HJtphKKKLZ0zHBix+xMJ/JP5hnz7O94+xqyjODhl6XpNtKgc2bDsPWpoejeUDAQK4lWDwu7eCl3+L/HCK5oEE7d01HcfudI1XD8Qr6E4PAyrKd71d65h66OtcwgQVojtWbMSditWHEubVQEssrmZGjOzmd/JzlUBEKQ5piJquQ0RTTGynQdKpLw29CakjpxVzT1uLvuvM89I21BDeXW2A6Gn54ay7Ov9aFGbp+wexlZqGpOZ0Pkw==,iv:3CWknBvAAt0Ls45kzAaeXSsiebkWWT2UdJhoyImVoHU=,tag:yy26Txkzf/Yof+Y8R5LcJA==,type:str] -pretix-banktool-cfg: ENC[AES256_GCM,data:6tcaQwnXsA2jZYQD2BdGu7gVzlCE+cF03icOF7VIVY92+xWMw+aivJRocDQAMBslD04EoEAu74tNIcky23swtgJwbtcwSmouDqofzo/HoXrudNyvjECYd6xUzmq/OvSBSQVY3s3OxCcc0TlpPVUipDxcGaALwJdhtCB11ZrVfOoXrycZ3YLAEESK0rC4tc5E550lhxmkyOan1m9q9rI4KVhv4mIlU6nVQbDA7LcljRPAJESFmwuwx75t6uPYKH9CjaYL3XE/MmZIZ6pN3qiNyqEx8dsxWZh1KQI233TfMr6jcIXwamTFK6/yieE2nQJfyM8r8b3E3D4nJ5+nn5Cb+wxwewEPveEmFUsNXYD84fpk2fK0nvYyOi32++gfefObFVE2ejAE7iu3rIrboA/wJoSfPCDGHVtdLVXC//MjLI3JUAU+QMTwRFz53KhE+lxFsbqaX4IdLuUwMoFSW6qRWGe7/SGUChH1Wrj705MMc4dfn2qaGhDbZNkDJA==,iv:Pl8XpuGcFzLlzEVe+JQ02S2rthKz88QMZXkGEywpCTU=,tag:beSgKDPcErhJhoV59YDIsw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1djjxl3lcvzs85nj0met6w8ujsz8pvr6ngmmdwlxfh0k9d5lkrpdqlzzehf - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0RXR4RnVQNjFvZ2NSZVhj - QVZva0lKS1RxM09sYmJjZE12NTBMd3NrUlNjCkV0aklndEZDM1BaWFhxYUJ5TDBG - T24zODBSdFVWV2VCNVZoM2s3RHJ4WHMKLS0tIC9LdDFMRW13YTBHNlVOdUY0b1NX - U3pyTDB4c1FWdHBPVjVjV3VpTjFWamsKDtc9C3xy/3Zu83+jQYCnHk8vatWANt4M - +Zo5kZ5yfYVSnvMvgpWoAHk/quXSLNg2YhKUDrYP5y57Q/jZTX3YbA== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-03-01T13:25:37Z" - mac: ENC[AES256_GCM,data:WcF4i8b+YpJuZj/hP8SEEvXJNlrf77ymNF6Avg4vt2JUkIoLh5EAMOjqPWWhJXS65rRSOCQOW/uRLoAMs3b1lX8r93u1wlzxnF5L/1RnAyTcCI2Aiadq6QjOKevgRwfc4vvTVN7LHKwZ9f8kCqgYiuOYtVDx3N4UPQ4SPJ3MZRw=,iv:iliNHU5y+YL2hpvWIltkhP6bkUonMakL7Ssdyf/be38=,tag:4YO93pGujwpHWjX5IAOQfw==,type:str] - pgp: - - created_at: "2023-07-08T09:50:21Z" - enc: | - -----BEGIN PGP MESSAGE----- - - hF4D3ylLYNOsO+0SAQdAMH1wIM+ENgeWlLsj7qUEorj8O1L5NlW9ABKB/Whmz3Ew - xm1SbZeFPPBPcT1dfVCF+W1CYDjrFau4DXhkcz5Z6x3ENg9rZujtRAZY9c+53aqD - 0l4B4zxls8vy0K/kipHn010WKhHEPMmABJf+d0rAkT6tbVzcxU3TKlZ2BWxwifM+ - BYDGZ2A6opgV8G4Q68n6CInyhMROIIzJJpWkP0YZCIzzVQ+9yelq9jZvuuxR7v9+ - =Lkul - -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 - unencrypted_suffix: _unencrypted - version: 3.7.3 diff --git a/hosts/weinturm-pretix-prod/services/default.nix b/hosts/weinturm-pretix-prod/services/default.nix deleted file mode 100644 index 731194c..0000000 --- a/hosts/weinturm-pretix-prod/services/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - imports = [ - ./pretix.nix - ]; -} diff --git a/hosts/weinturm-pretix-prod/services/pretix.nix b/hosts/weinturm-pretix-prod/services/pretix.nix deleted file mode 100644 index 72ef2df..0000000 --- a/hosts/weinturm-pretix-prod/services/pretix.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ config, lib, pkgs, ... }: -{ - services.pretix = { - enable = true; - instanceName = "Weinturm Open Air"; - domain = "tickets.weinturm-open-air.de"; - enableTls = true; - enableRegistration = false; - passwordReset = true; - locale = "de"; - timezone = "Europe/Berlin"; - secretsFile = ../secrets.yaml; - banktool = { - enable = true; - days = 14; - }; - }; - - security.acme = { - acceptTerms = true; - defaults.email = lib.mkForce "helfer@weinturm-open-air.de"; - }; -} diff --git a/justfile b/justfile index b44ce6d..d1d2a9a 100644 --- a/justfile +++ b/justfile @@ -1,14 +1,34 @@ +usb_ram_disk := "/dev/disk/by-label/RAM_USB" +usb_ram_mountpoint := shell("findmnt -n -o TARGET $1 || true", usb_ram_disk) + boot: - nixos-rebuild boot --flake . --use-remote-sudo - which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true + nixos-rebuild boot --flake . --use-remote-sudo + which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true switch: - nixos-rebuild switch --flake . --use-remote-sudo - which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true + nixos-rebuild switch --flake . --use-remote-sudo + which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true build: - nixos-rebuild build --flake . + nixos-rebuild build --flake . update: - nix flake update --commit-lock-file - which fwupdmgr >/dev/null 2>&1 && fwupdmgr refresh || true + nix flake update --commit-lock-file + which fwupdmgr >/dev/null 2>&1 && fwupdmgr refresh || true + +repl: + nix repl --expr "\ + let \ + flake = builtins.getFlake \"$(git rev-parse --show-toplevel)\"; in \ + flake // (with flake; { \ + lib = inputs.nixpkgs.lib; \ + pkgs = inputs.nixpkgs.legacyPackages."\${builtins.currentSystem}".extend(import ./pkgs inputs); \ + }) \ + " + +luks-pass host: + @if [ -d "{{usb_ram_mountpoint}}" ]; then \ + gpg -d hosts/{{host}}/luks-passfile.gpg > "{{usb_ram_mountpoint}}/{{host}}.key"; \ + else \ + echo "Mount point not found. Is the usb device plugged and mounted?" >&2; \ + fi diff --git a/modules/adb.nix b/modules/adb.nix new file mode 100644 index 0000000..47198c1 --- /dev/null +++ b/modules/adb.nix @@ -0,0 +1,5 @@ +{ config, lib, ... }: + +lib.mkIf config.jalr.gui.enable { + programs.adb.enable = true; +} diff --git a/modules/avahi.nix b/modules/avahi.nix new file mode 100644 index 0000000..c12bd75 --- /dev/null +++ b/modules/avahi.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +lib.mkIf config.jalr.gui.enable { + services.avahi = { + enable = true; + package = + let + xmltoman = pkgs.xmltoman.overrideAttrs (_: { + nativeBuildInputs = [ + pkgs.installShellFiles + ]; + buildInputs = [ + (pkgs.perl.withPackages (pl: [ + pl.XMLParser + ])) + ]; + }); + in + pkgs.avahi.overrideAttrs (o: rec { + version = "0.9-rc2"; + src = pkgs.fetchurl { + url = "https://github.com/avahi/avahi/archive/refs/tags/v${version}.tar.gz"; + sha256 = "sha256-9k7+1qlyz5LLLfs1q/aqkXPWK4Q7FYUML0CvdqQjj4o="; + }; + patches = [ ]; + buildInputs = o.buildInputs ++ [ pkgs.systemdLibs ]; + nativeBuildInputs = o.nativeBuildInputs ++ [ xmltoman ]; + installFlags = [ + "runstatedir=${placeholder "out"}/run" + "sysconfdir=${placeholder "out"}/etc" + ]; + }); + nssmdns4 = true; + extraConfig = '' + [server] + ratelimit-interval-usec=500000 + ratelimit-burst=500 + [wide-area] + enable-wide-area=no + ''; + }; +} diff --git a/modules/aws.nix b/modules/aws.nix index f94580a..2b6eddb 100644 --- a/modules/aws.nix +++ b/modules/aws.nix @@ -4,7 +4,7 @@ options.jalr.aws = { enable = lib.mkEnableOption "Enable AWS CLI"; accounts = with lib; mkOption { - type = with types; attrsOf (submodule ({ config, name, ... }: { + type = with types; attrsOf (submodule (_: { options = { sso_account_id = mkOption { type = int; diff --git a/modules/bluetooth.nix b/modules/bluetooth.nix new file mode 100644 index 0000000..6c478b7 --- /dev/null +++ b/modules/bluetooth.nix @@ -0,0 +1,20 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.jalr; +in +{ + options.jalr = { + bluetooth.enable = pkgs.lib.mkEnableOption "Enable bluetooth" // { default = false; }; + }; + config = lib.mkIf cfg.bluetooth.enable { + hardware.bluetooth.enable = true; + services = { + blueman.enable = true; + ofono.enable = true; + upower.enable = true; + }; + hardware.bluetooth.settings.General.Experimental = true; # to show battery state + }; +} + diff --git a/modules/bootloader/default.nix b/modules/bootloader/default.nix index 22c07a5..bcb2908 100644 --- a/modules/bootloader/default.nix +++ b/modules/bootloader/default.nix @@ -1,15 +1,16 @@ -{ config, lib, pkgs, ... }: +{ lib, ... }: { options.jalr = { bootloader = lib.mkOption { - type = lib.types.nullOr (lib.types.enum [ "systemd-boot" "grub2" ]); + type = lib.types.nullOr (lib.types.enum [ "systemd-boot" "grub2" "lanzaboote" ]); default = null; description = "Bootloader to install"; }; }; imports = [ - ./systemd-boot.nix ./grub2.nix + ./lanzaboote.nix + ./systemd-boot.nix ]; } diff --git a/modules/bootloader/grub2.nix b/modules/bootloader/grub2.nix index a2ec2ee..2037be7 100644 --- a/modules/bootloader/grub2.nix +++ b/modules/bootloader/grub2.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: lib.mkIf (config.jalr.bootloader == "grub2") { boot.loader.grub = { diff --git a/modules/bootloader/lanzaboote.nix b/modules/bootloader/lanzaboote.nix new file mode 100644 index 0000000..92f0820 --- /dev/null +++ b/modules/bootloader/lanzaboote.nix @@ -0,0 +1,8 @@ +{ config, lib, ... }: + +lib.mkIf (config.jalr.bootloader == "lanzaboote") { + boot.lanzaboote = { + enable = true; + pkiBundle = "/etc/secureboot"; + }; +} diff --git a/modules/bootloader/systemd-boot.nix b/modules/bootloader/systemd-boot.nix index 96cab4d..995bb0b 100644 --- a/modules/bootloader/systemd-boot.nix +++ b/modules/bootloader/systemd-boot.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: lib.mkIf (config.jalr.bootloader == "systemd-boot") { boot = { diff --git a/modules/debug.nix b/modules/debug.nix new file mode 100644 index 0000000..9cc70b5 --- /dev/null +++ b/modules/debug.nix @@ -0,0 +1,16 @@ +{ config, lib, ... }: +let + cfg = config.jalr.debug; +in +{ + options.jalr.debug = { + enable = lib.mkEnableOption "debugging helpers, DO NOT USE IN PRODUCTION!"; + }; + + config = lib.mkIf cfg.enable { + services.getty.autologinUser = "root"; + boot.initrd.systemd.emergencyAccess = true; + systemd.enableEmergencyMode = true; + boot.kernelParams = [ "systemd.setenv=SYSTEMD_SULOGIN_FORCE=1" ]; + }; +} diff --git a/modules/default.nix b/modules/default.nix index 7ecebb6..cd6a1cf 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -2,49 +2,72 @@ { options.jalr = { - gui.enable = lib.mkEnableOption "GUI"; + gui = { + enable = lib.mkEnableOption "GUI"; + sway.enable = lib.mkEnableOption "sway"; + gnome.enable = lib.mkEnableOption "gnome"; + }; workstation.enable = lib.mkEnableOption "Workstation"; }; imports = [ ../pkgs/modules.nix + ./adb.nix ./autologin.nix + ./avahi.nix ./aws.nix + ./bluetooth.nix ./bootloader - ./dji-goggles.nix - ./dnsmasq.nix + ./debug.nix + ./dns.nix + ./esphome ./fish.nix ./fonts.nix + ./gnome.nix ./journald.nix ./kdeconnect.nix ./kvm-switch-enable-screen.nix ./libvirt.nix ./localization.nix + ./luksusb.nix ./mailserver + ./matrix + ./mobile-network.nix ./mute-indicator.nix + ./neo.nix + ./networking ./nix.nix ./obs.nix ./pipewire.nix ./podman.nix ./printers - ./qbittorrent - ./sdr.nix + ./remarkable.nix ./sshd.nix + ./steelseries-nova-pro.nix ./sudo.nix ./sway.nix - ./tor.nix - ./tradebyte - ./udmx.nix + ./udev.nix ./uefi.nix ./unfree.nix - ./wireshark.nix + ./upgrade-diff.nix + ./wireshark ./yubikey-gpg.nix ]; config = { - boot.tmp.cleanOnBoot = true; + boot = { + tmp.cleanOnBoot = true; + kernel.sysctl = { + "kernel.kptr_restrict" = 1; + "kernel.yama.ptrace_scope" = 1; + "kernel.kexec_load_disabled" = 1; + }; + kernelParams = [ + "lockdown=integrity" + ]; + }; - security.polkit.enable = true; + programs.nano.enable = false; security.acme = { acceptTerms = true; diff --git a/modules/dji-goggles.nix b/modules/dji-goggles.nix deleted file mode 100644 index 00a735b..0000000 --- a/modules/dji-goggles.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ - services.udev.extraRules = '' - # DJI Goggles - SUBSYSTEM=="usb", ATTRS{idVendor}=="2ca3", ATTRS{idProduct}=="001f", GROUP="video", MODE="0660" - ''; -} diff --git a/modules/dns.nix b/modules/dns.nix new file mode 100644 index 0000000..a15d066 --- /dev/null +++ b/modules/dns.nix @@ -0,0 +1,50 @@ +{ lib, config, ... }: + +let + dnscryptListenAddress = "127.0.0.1"; + dnscryptListenPort = 9053; +in +{ + config = lib.mkIf config.jalr.workstation.enable { + services.dnscrypt-proxy2 = { + enable = true; + settings = { + ipv6_servers = true; + require_dnssec = true; + require_nolog = true; + require_nofilter = true; + dnscrypt_ephemeral_keys = true; + tls_disable_session_tickets = true; + listen_addresses = [ "${dnscryptListenAddress}:${toString dnscryptListenPort}" ]; + anonymized_dns.skip_incompatible = true; + }; + }; + services.dnsmasq = { + enable = true; + resolveLocalQueries = true; + settings = { + server = [ + "/iceportal.de/172.18.0.1" + "/lab.fablab-nea.de/192.168.94.1" + "/iot.bw.jalr.de/192.168.42.1" + "/lan.bw.jalr.de/192.168.42.1" + "/lechner.zz/192.168.0.1" + "/login.wifionice.de/172.18.0.1" + "${dnscryptListenAddress}#${toString dnscryptListenPort}" + ]; + address = [ + "/localhost/127.0.0.1" + ]; + no-resolv = true; + interface = "lo"; + listen-address = [ + "::1" + "127.0.0.1" + ]; + bind-interfaces = true; + dns-loop-detect = true; + neg-ttl = 5; + }; + }; + }; +} diff --git a/modules/dnsmasq.nix b/modules/dnsmasq.nix deleted file mode 100644 index 3e1d7ab..0000000 --- a/modules/dnsmasq.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ lib, config, ... }: - -{ - config = lib.mkIf config.jalr.workstation.enable { - services.dnsmasq = { - enable = true; - resolveLocalQueries = true; - settings = { - server = [ - "127.0.0.1#9053" - "/lechner.zz/192.168.0.1" - "/lab.fablab-nea.de/192.168.94.1" - ]; - no-resolv = true; - interface = "lo"; - listen-address = [ - "::1" - "127.0.0.1" - ]; - bind-interfaces = true; - dns-loop-detect = true; - neg-ttl = 5; - }; - }; - }; -} diff --git a/modules/esphome/default.nix b/modules/esphome/default.nix new file mode 100644 index 0000000..4edf71e --- /dev/null +++ b/modules/esphome/default.nix @@ -0,0 +1,100 @@ +{ lib +, pkgs +, config +, ... +}: +let + cfg = config.jalr.esphome; + esphomeParams = + if config.services.esphome.enableUnixSocket + then "--socket /run/esphome/esphome.sock" + else "--address ${config.services.esphome.address} --port ${toString config.services.esphome.port}"; +in +{ + options.jalr.esphome = with lib; with lib.types; { + enable = mkEnableOption "ESPHome"; + port = mkOption { + description = "TCP port for esphome dashboard."; + type = port; + }; + configDir = mkOption { + type = path; + description = "Location of the device configuration"; + }; + secretsFile = mkOption { + type = path; + description = "Location of the secrets file"; + }; + }; + + config = lib.mkIf cfg.enable { + services.esphome = { + enable = true; + address = "127.0.0.1"; + inherit (cfg) port; + package = pkgs.master.esphome; + }; + + systemd.services.esphome = { + environment = { + "PLATFORMIO_CORE_DIR" = lib.mkForce "/var/cache/esphome/.platformio"; + }; + serviceConfig = { + BindReadOnlyPaths = [ + "/nix/store" + "%d/secrets.yaml:/run/esphome/config/secrets.yaml" + "/etc/resolv.conf" + "/etc/ssl" + "/etc/static/ssl" + ]; + TemporaryFileSystem = [ + "/var/lib" + ]; + ExecPaths = [ + "-+/var/cache/esphome/.platformio/packages/" + ]; + DeviceAllow = [ + "char-ttyACM rw" + "char-ttyAMA rw" + "char-ttyUSB rw" + ]; + ExecStartPre = [ + (pkgs.writeShellScript "esphome-exec-start-pre" '' + if ! [ -d "$CACHE_DIRECTORY/.platformio/packages" ]; then + mkdir -p "$CACHE_DIRECTORY/.platformio/packages" + exit 1 + fi + mkdir -p "$CACHE_DIRECTORY/.esphome" + for linked in \ + .esphome \ + .gitignore + do + ln -s "$CACHE_DIRECTORY/$linked" "/run/esphome/config/$linked" + done + ${pkgs.rsync}/bin/rsync \ + -a \ + --delete \ + --checksum \ + --exclude secrets.yaml \ + --exclude=.esphome \ + --exclude=.platformio \ + --exclude=.gitignore \ + '${cfg.configDir}/' /run/esphome/config/ + '') + ]; + ExecStart = lib.mkForce "${config.services.esphome.package}/bin/esphome dashboard ${esphomeParams} /run/esphome/config"; + LoadCredential = "secrets.yaml:${cfg.secretsFile}"; + PrivateTmp = true; + RootDirectory = "%t/esphome/chroot"; + RuntimeDirectory = [ + "esphome/chroot" + "esphome/config" + ]; + StateDirectory = lib.mkForce [ ]; + CacheDirectory = "esphome"; + SupplementaryGroups = [ "dialout" ]; + WorkingDirectory = lib.mkForce "/run/esphome/config"; + }; + }; + }; +} diff --git a/modules/esphome/devices/justfile b/modules/esphome/devices/justfile new file mode 100644 index 0000000..2c86b69 --- /dev/null +++ b/modules/esphome/devices/justfile @@ -0,0 +1,17 @@ +import '../../../../../justfile' +set dotenv-load + +download: + rsync \ + -r \ + --rsync-path='sudo rsync' \ + --exclude '/build' \ + --exclude '/.esphome' \ + --exclude '.gitignore' \ + --exclude 'secrets.yaml' \ + ${ESPHOME_HOST}:/var/lib/esphome/ ./ + +download-secrets: + umask 0077 && ssh ${ESPHOME_HOST} sudo cat /run/secrets/esphome > "/dev/shm/${ESPHOME_SECRETS_FILE}" + ln -sf "/dev/shm/${ESPHOME_SECRETS_FILE}" "{{justfile_directory()}}/secrets.yaml" + diff --git a/modules/fonts.nix b/modules/fonts.nix index 656530b..2225251 100644 --- a/modules/fonts.nix +++ b/modules/fonts.nix @@ -2,8 +2,11 @@ { console.font = "Lat2-Terminus16"; - fonts.fonts = with pkgs; lib.mkIf config.jalr.gui.enable [ - (nerdfonts.override { fonts = [ "Iosevka" ]; }) + fonts.packages = with pkgs; lib.mkIf config.jalr.gui.enable [ + nerd-fonts.fira-code + nerd-fonts.iosevka + nerd-fonts.iosevka-term + nerd-fonts.iosevka-term-slab font-awesome powerline-fonts roboto diff --git a/modules/gnome.nix b/modules/gnome.nix new file mode 100644 index 0000000..1a6b784 --- /dev/null +++ b/modules/gnome.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: + +lib.mkIf (config.jalr.gui.enable && config.jalr.gui.gnome.enable) { + services.xserver = { + enable = true; + desktopManager.gnome.enable = true; + displayManager.gdm = { + enable = true; + autoSuspend = false; + }; + exportConfiguration = true; + }; + + /* + programs.dconf = { + enable = true; + profiles = { + user.databases = [{ + settings = with lib.gvariant; { + "org/gnome/desktop/input-sources" = { + sources = [ + (mkTuple [ "xkb" "de" ]) + (mkTuple [ "xkb" "de+neo" ]) + (mkTuple [ "xkb" "us" ]) + ]; + }; + }; + }]; + }; + }; + */ +} diff --git a/modules/journald.nix b/modules/journald.nix index c6f1271..e527dc6 100644 --- a/modules/journald.nix +++ b/modules/journald.nix @@ -1,5 +1,3 @@ -{ lib, ... }: - { services.journald.extraConfig = '' MaxRetentionSec=90day diff --git a/modules/kdeconnect.nix b/modules/kdeconnect.nix index 87cbf5d..96b3016 100644 --- a/modules/kdeconnect.nix +++ b/modules/kdeconnect.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: let portRange = { diff --git a/modules/localization.nix b/modules/localization.nix index 3c3d813..0034c23 100644 --- a/modules/localization.nix +++ b/modules/localization.nix @@ -1,3 +1,4 @@ +{ config, ... }: { i18n = { defaultLocale = "en_GB.UTF-8"; @@ -8,7 +9,7 @@ console.keyMap = "neo"; - time.timeZone = "UTC"; + time.timeZone = if config.jalr.workstation.enable then "Europe/Berlin" else "UTC"; location = { latitude = 49.5; diff --git a/modules/luksusb.nix b/modules/luksusb.nix new file mode 100644 index 0000000..315c6a0 --- /dev/null +++ b/modules/luksusb.nix @@ -0,0 +1,121 @@ +{ config, lib, ... }: +let + cfg = config.jalr.luksUsbUnlock; +in +{ + options.jalr.luksUsbUnlock = with lib; with lib.types; { + enable = mkEnableOption "unlock LUKS volumes with a USB device on boot"; + devices = mkOption { + default = { }; + example = { + cryptroot = { + keyPath = "/path/to/the/key"; + usbDevice = "by-label/MY_USB"; + }; + }; + type = types.attrsOf (types.submodule { + options = { + keyPath = mkOption { + example = "/mykey.key"; + description = mdDoc '' + Path to the key file inside the USB device's filesystem. + `/` is relative to the device's filesystem root. + ''; + type = types.str; + }; + + usbDevice = mkOption { + example = "by-label/BOOTKEY"; + description = mdDoc '' + Path to the USB device that contains the keys. (Path relative to `/dev/disk/`) + ''; + type = types.str; + }; + + waitForDevice = mkOption { + default = 5; + example = 10; + description = mdDoc '' + How many seconds to wait for the USB device to be detected by the + kernel. + ''; + type = types.ints.unsigned; + }; + }; + }); + }; + }; + config = lib.mkIf cfg.enable ( + let + makeUsbDevPath = usbDevice: "/dev/disk/" + usbDevice; + makeMountPath = usbDevice: "/key/" + (builtins.hashString "md5" usbDevice); + usbFsType = "vfat"; + + mapAttrsNameValue = f: set: + lib.listToAttrs (map f (lib.attrsToList set)); + in + { + boot.initrd = { + kernelModules = [ "uas" "usbcore" "usb_storage" "vfat" "nls_cp437" "nls_iso8859_1" ]; + systemd.services = + let + makeService = name: { usbDevice, waitForDevice, ... }: + let + usbDevPath = makeUsbDevPath usbDevice; + usbMountPath = makeMountPath usbDevice; + in + { + description = "Mount ${name} key"; + wantedBy = [ "cryptsetup.target" ]; + before = [ "systemd-cryptsetup@${name}.service" ]; + after = [ "systemd-modules-load.service" ]; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + + script = '' + if awk -v mountpoint="${usbMountPath}" '$2==mountpoint {f=1} END {exit !f}' /proc/mounts; then + exit 0 + fi + + attempts=0 + while [ ! -e ${lib.escapeShellArg usbDevPath} ]; do + sleep 1 + if [ $attempts -ge ${toString waitForDevice} ]; then + break; + fi + attempts=$((attempts+1)) + done + + if [ -e ${lib.escapeShellArg usbDevPath} ]; then + mkdir -m0500 -p ${lib.escapeShellArg usbMountPath} + mount \ + -n \ + -t ${lib.escapeShellArg usbFsType} \ + -o ro,fmask=0137,dmask=0027 \ + ${lib.escapeShellArg usbDevPath} \ + ${lib.escapeShellArg usbMountPath} + fi + ''; + }; + in + mapAttrsNameValue + ({ name, value }: { + name = "luksusb-${name}"; + value = makeService name value; + }) + cfg.devices; + + luks.devices = builtins.mapAttrs + (_: { keyPath, usbDevice, ... }: + let + usbMountPath = makeMountPath usbDevice; + in + { + keyFile = "${usbMountPath}/${keyPath}"; + keyFileTimeout = 1; + }) + cfg.devices; + }; + } + ); +} diff --git a/modules/mailserver/default.nix b/modules/mailserver/default.nix index 353d14a..59a8e2c 100644 --- a/modules/mailserver/default.nix +++ b/modules/mailserver/default.nix @@ -1,10 +1,15 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: let cfg = config.jalr.mailserver; in { options.jalr.mailserver = with lib; with lib.types; { enable = mkEnableOption "simple mail server"; + relayPort = mkOption { + description = "SMTP port for relay mail relay."; + type = port; + default = 25; + }; fqdn = mkOption { type = str; description = '' diff --git a/modules/mailserver/dovecot.nix b/modules/mailserver/dovecot.nix index 353474d..a2485a5 100644 --- a/modules/mailserver/dovecot.nix +++ b/modules/mailserver/dovecot.nix @@ -13,8 +13,6 @@ lib.mkIf cfg.enable { services.dovecot2 = { enable = true; - modules = with pkgs; [ dovecot_pigeonhole ]; - enableLmtp = true; enablePAM = false; @@ -33,14 +31,24 @@ lib.mkIf cfg.enable { Spam = { specialUse = "Junk"; auto = "subscribe"; }; }; - sieveScripts = { - before = pkgs.writeText "spam.sieve" '' - require "fileinto"; + sieve = { + globalExtensions = [ + "fileinto" + "vnd.dovecot.pipe" + ]; + plugins = [ + "sieve_imapsieve" + "sieve_extprograms" + ]; + scripts = { + before = pkgs.writeText "spam.sieve" '' + require "fileinto"; - if header :is "X-Spam" "Yes" { - fileinto "Spam"; - } - ''; + if header :is "X-Spam" "Yes" { + fileinto "Spam"; + } + ''; + }; }; extraConfig = '' @@ -100,8 +108,6 @@ lib.mkIf cfg.enable { lda_mailbox_autocreate = yes plugin { - sieve_plugins = sieve_imapsieve sieve_extprograms - ${lib.optionalString cfg.spam.enable '' imapsieve_mailbox1_name = Spam imapsieve_mailbox1_causes = COPY @@ -113,11 +119,12 @@ lib.mkIf cfg.enable { imapsieve_mailbox2_before = file:/var/lib/dovecot/sieve/learn-ham.sieve sieve_pipe_bin_dir = ${pkgs.symlinkJoin { name = "sieve-pipe-bin-dir"; paths = with pkgs; [ rspamd ]; } }/bin ''} - - sieve_global_extensions = +vnd.dovecot.pipe } ''; }; + + environment.systemPackages = [ pkgs.dovecot_pigeonhole ]; + systemd.services.dovecot2 = { wants = [ "acme-finished-${cfg.fqdn}.target" ]; after = [ "acme-finished-${cfg.fqdn}.target" ]; diff --git a/modules/mailserver/postfix.nix b/modules/mailserver/postfix.nix index e5a8831..e813dcb 100644 --- a/modules/mailserver/postfix.nix +++ b/modules/mailserver/postfix.nix @@ -5,26 +5,26 @@ let listToString = lib.concatStringsSep ","; # List of attribute sets with single key-value pair - plainAliases = (lib.flatten + plainAliases = lib.flatten (map ({ address, aliases, ... }: map (alias: { "${alias}" = address; }) (aliases ++ lib.singleton address)) - cfg.users)); + cfg.users); # Attribute set with every alias mapped to a list of receivers - mergedAliases = (lib.attrsets.foldAttrs + mergedAliases = lib.attrsets.foldAttrs (val: col: lib.singleton val ++ col) [ ] - plainAliases); + plainAliases; # Contents of the aliases file - aliasesString = (lib.concatStringsSep + aliasesString = lib.concatStringsSep "\n" (lib.mapAttrsToList (alias: addresses: "${alias} ${listToString addresses}") - mergedAliases)); + mergedAliases); valiases = pkgs.writeText "valiases" aliasesString; @@ -35,11 +35,12 @@ let cfg.cleanHeaders); in lib.mkIf cfg.enable { - security.dhparams.params.postfix = { }; services.postfix = { enable = true; - enableSubmission = true; # plain/STARTTLS (latter is forced in submissionOptions) + inherit (cfg) relayPort; + + enableSubmission = false; # plain/STARTTLS (latter is forced in submissionOptions) enableSubmissions = true; # submission with implicit TLS (TCP/465) hostname = cfg.fqdn; @@ -68,13 +69,11 @@ lib.mkIf cfg.enable { smtpd_recipient_restrictions = listToString [ "reject_non_fqdn_recipient" - "reject_rbl_client ix.dnsbl.manitu.net" "reject_unknown_recipient_domain" "reject_unverified_recipient" ]; smtpd_client_restrictions = listToString [ - "reject_rbl_client ix.dnsbl.manitu.net" "reject_unknown_client_hostname" ]; @@ -103,8 +102,6 @@ lib.mkIf cfg.enable { "DHE-RSA-AES256-GCM-SHA384" ]; tls_preempt_cipherlist = "no"; - - smtpd_tls_dh1024_param_file = config.security.dhparams.params.postfix.path; }; # plain/STARTTLS (forced with smtpd_tls_security_level) @@ -145,7 +142,7 @@ lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 25 # SMTP - 587 # SMTP submission + 465 # SMTPS (implicit TLS) ]; systemd.services.postfix = { diff --git a/modules/mailserver/rspamd.nix b/modules/mailserver/rspamd.nix index a7e0992..ee205de 100644 --- a/modules/mailserver/rspamd.nix +++ b/modules/mailserver/rspamd.nix @@ -6,7 +6,7 @@ let # nix shell nixpkgs#rspamd -c \ # rspamadm dkim_keygen -s default -d example.com -b 4096 -k /dev/shm/dkim.key > dkim.txt - dkimEnabledDomains = (lib.filter (d: d.enableDKIM) cfg.domains); + dkimEnabledDomains = lib.filter (d: d.enableDKIM) cfg.domains; dkimSignatureDir = pkgs.stdenvNoCC.mkDerivation { name = "dkim-signatures"; dontUnpack = true; diff --git a/modules/matrix/default.nix b/modules/matrix/default.nix new file mode 100644 index 0000000..4f3c745 --- /dev/null +++ b/modules/matrix/default.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: + +{ + options.jalr.matrix = with lib; with lib.types; { + enable = mkEnableOption "simple matrix server"; + synapse = { + port = mkOption { + description = "TCP port for synapse service."; + type = port; + }; + app_service_config = mkOption { + type = attrsOf path; + description = '' + An attribute set of app_service_config_files + ''; + default = { }; + example = { + "my-service-alias" = "/path/to/appservice.yaml"; + }; + }; + }; + fqdn = mkOption { + type = str; + description = '' + FQDN of the matrix server + ''; + example = "matrix.example.com"; + }; + domain = mkOption { + type = str; + description = '' + Domain of the matrix server + ''; + example = "example.com"; + }; + turn = { + host = mkOption { + type = str; + description = '' + Hostname of TURN service + ''; + example = "turn.example.com"; + }; + sharedSecretFile = mkOption { + type = path; + description = "Location of the shared secret file for the TURN service"; + }; + }; + mautrix-signal = { + enable = mkEnableOption "signal bridge"; + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit; + defaultText = literalExpression '' + optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit + ''; + description = '' + List of Systemd services to require and wait for when starting the application service. + ''; + }; + port = mkOption { + description = "TCP port for mautrix-signal."; + type = port; + }; + settings = mkOption { + inherit ((pkgs.formats.json { })) type; + }; + }; + mautrix-whatsapp = { + enable = mkEnableOption "whatsapp bridge"; + port = mkOption { + description = "TCP port for mautrix-whatsapp."; + type = port; + }; + settings = mkOption { + inherit ((pkgs.formats.json { })) type; + }; + }; + }; + imports = [ + ./mautrix-signal.nix + ./mautrix-whatsapp.nix + ./synapse.nix + ]; +} diff --git a/modules/matrix/mautrix-signal.nix b/modules/matrix/mautrix-signal.nix new file mode 100644 index 0000000..e4b50d1 --- /dev/null +++ b/modules/matrix/mautrix-signal.nix @@ -0,0 +1,49 @@ +{ config, lib, ... }: + +let + cfg = config.jalr.matrix; + synapseCfg = config.services.matrix-synapse.settings; + dataDir = "/var/lib/mautrix-signal"; +in +lib.mkIf cfg.mautrix-signal.enable { + services.mautrix-signal = { + enable = true; + registerToSynapse = true; + settings = { + homeserver = { + address = synapseCfg.public_baseurl; + domain = synapseCfg.server_name; + }; + database = { + type = "sqlite3-fk-wal"; + uri = "file:${dataDir}/mautrix-signal.db?_txlock=immediate"; + }; + appservice = rec { + hostname = "127.0.0.1"; + inherit (cfg.mautrix-signal) port; + address = "http://${hostname}:${toString port}"; + provisioning.shared_secret = "disable"; + }; + bridge = { + encryption = { + allow = true; + default = true; + }; + verification_levels = { + receive = "cross-signed-tofu"; + send = "cross-signed-tofu"; + share = "cross-signed-tofu"; + }; + }; + logging = { + version = 1; + min_level = "info"; + writers = lib.singleton { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + }; + }; + } // cfg.mautrix-signal.settings; + }; +} diff --git a/modules/matrix/mautrix-whatsapp.nix b/modules/matrix/mautrix-whatsapp.nix new file mode 100644 index 0000000..7225227 --- /dev/null +++ b/modules/matrix/mautrix-whatsapp.nix @@ -0,0 +1,62 @@ +{ config, lib, ... }: + +let + cfg = config.jalr.matrix; + synapseCfg = config.services.matrix-synapse.settings; +in +lib.mkIf cfg.mautrix-whatsapp.enable { + services.mautrix-whatsapp = { + enable = true; + registerToSynapse = true; + settings = lib.mkForce ({ + homeserver = { + address = synapseCfg.public_baseurl; + domain = synapseCfg.server_name; + }; + database = { + type = "sqlite3-fk-wal"; + uri = "file:/var/lib/mautrix-whatsapp/mautrix-whatsapp.db?_txlock=immediate"; + }; + appservice = rec { + hostname = "127.0.0.1"; + inherit (cfg.mautrix-whatsapp) port; + address = "http://${hostname}:${toString port}"; + provisioning.shared_secret = "disable"; + id = "whatsapp"; + bot = { + username = "whatsappbot"; + displayname = "WhatsApp bridge bot"; + avatar = "mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr"; + }; + }; + whatsapp = { + browser_name = "mx-wa"; + os_name = "Mautrix-WhatsApp bridge"; + }; + bridge = { + command_prefix = "!wa"; + delivery_receipts = true; + displayname_template = "{{if .FullName}}{{.FullName}}{{else if .Notify}}{{.Notify}}{{else}}{{.Jid}}{{end}} (WA)"; + history_sync = { + backfill = true; + }; + identity_change_notices = true; + private_chat_portal_meta = true; + reaction_notices = true; + relay.enable = false; + }; + logging = { + file_name_format = null; + min_level = "info"; + print_level = "info"; + writers = [ + { + format = "pretty-colored"; + time_format = " "; + type = "stdout"; + } + ]; + }; + } // cfg.mautrix-whatsapp.settings); + }; +} diff --git a/modules/matrix/synapse.nix b/modules/matrix/synapse.nix new file mode 100644 index 0000000..ea3ecc0 --- /dev/null +++ b/modules/matrix/synapse.nix @@ -0,0 +1,149 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.jalr.matrix; +in +lib.mkIf cfg.enable { + services = { + matrix-synapse = { + enable = true; + + settings = { + server_name = cfg.domain; + public_baseurl = "https://${cfg.fqdn}"; + + database = { + name = "psycopg2"; + args.user = "matrix-synapse"; + args.database = "matrix-synapse"; + }; + + listeners = lib.singleton { + inherit (cfg.synapse) port; + bind_addresses = [ "127.0.0.1" "::1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = lib.singleton { + names = [ "client" "federation" "metrics" ]; + compress = false; + }; + }; + + turn_uris = [ + "turns:${cfg.turn.host}:5349?transport=udp" + "turns:${cfg.turn.host}:5349?transport=tcp" + "turn:${cfg.turn.host}:3478?transport=udp" + "turn:${cfg.turn.host}:3478?transport=tcp" + ]; + turn_user_lifetime = "1h"; + + enable_metrics = true; + + # adapted from https://github.com/NixOS/nixpkgs/blob/7e10bf4327491a6ebccbe1aaa8e6c6c0aca4663a/nixos/modules/services/misc/matrix-synapse-log_config.yaml + # - set root.level to WARNING instead of INFO + log_config = pkgs.writeText "log_config.yaml" (builtins.toJSON { + version = 1; + + formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; + + filters.context = { + "()" = "synapse.util.logcontext.LoggingContextFilter"; + request = ""; + }; + + handlers.journal = { + class = "systemd.journal.JournalHandler"; + formatter = "journal_fmt"; + filters = [ "context" ]; + SYSLOG_IDENTIFIER = "synapse"; + }; + + root = { + level = "WARNING"; + handlers = [ "journal" ]; + }; + + disable_existing_loggers = false; + }); + + max_upload_size = "50M"; + + # I’m okay with using matrix.org as trusted key server + suppress_key_server_warning = true; + + # For mautrix-whatsapp backfilling + experimental_features.msc2716_enabled = true; + }; + + extraConfigFiles = [ + cfg.turn.sharedSecretFile + ]; + }; + + matrix-synapse.settings.app_service_config_files = lib.attrsets.mapAttrsToList + ( + name: _: + "/run/matrix-synapse/app_service_config/${name}.yaml" + ) + cfg.synapse.app_service_config; + + nginx.virtualHosts = { + "${cfg.fqdn}" = { + enableACME = true; + forceSSL = true; + + locations."/_matrix" = + let + listenerCfg = lib.elemAt config.services.matrix-synapse.settings.listeners 0; + in + { + proxyPass = "http://${lib.elemAt listenerCfg.bind_addresses 0}:${toString listenerCfg.port}"; + + extraConfig = '' + client_max_body_size ${config.services.matrix-synapse.settings.max_upload_size}; + ''; + }; + }; + + }; + + postgresql = { + enable = true; + ensureDatabases = [ + config.services.matrix-synapse.settings.database.args.database + ]; + ensureUsers = [{ + name = config.services.matrix-synapse.settings.database.args.user; + ensureDBOwnership = true; + }]; + }; + }; + + systemd.services.matrix-synapse = { + restartTriggers = lib.attrsets.mapAttrsToList + ( + _: value: "${value}" + ) + cfg.synapse.app_service_config; + serviceConfig = { + RuntimeDirectory = lib.mkForce [ + "matrix-synapse" + "matrix-synapse/app_service_config" + ]; + RuntimeDirectoryPreserve = lib.mkForce false; + ExecStartPre = lib.attrsets.mapAttrsToList + (name: value: + let + script = pkgs.writeShellScript "app_service_config-${name}" + '' + cp "${value}" "/run/matrix-synapse/app_service_config/${name}.yaml" + chown matrix-synapse: "/run/matrix-synapse/app_service_config/${name}.yaml" + ''; + in + "+${script}" + ) + cfg.synapse.app_service_config; + }; + }; +} diff --git a/modules/mobile-network.nix b/modules/mobile-network.nix new file mode 100644 index 0000000..fb7efdb --- /dev/null +++ b/modules/mobile-network.nix @@ -0,0 +1,7 @@ +{ config, lib, pkgs, ... }: + +lib.mkIf config.jalr.gui.enable { + environment.systemPackages = with pkgs; [ + usb-modeswitch + ]; +} diff --git a/home-manager/modules/neo.nix b/modules/neo.nix similarity index 71% rename from home-manager/modules/neo.nix rename to modules/neo.nix index 48bbb3b..b9c299c 100644 --- a/home-manager/modules/neo.nix +++ b/modules/neo.nix @@ -1,6 +1,5 @@ -{ config, pkgs, ... }: { - home.sessionVariables = { + environment.variables = { XKB_DEFAULT_LAYOUT = "de,de"; XKB_DEFAULT_VARIANT = "neo,"; XKB_DEFAULT_OPTIONS = "grp:win_space_toggle"; diff --git a/modules/networking/default.nix b/modules/networking/default.nix new file mode 100644 index 0000000..80cdff2 --- /dev/null +++ b/modules/networking/default.nix @@ -0,0 +1,14 @@ +{ lib +, ... +}: + +{ + imports = [ + ./network-manager.nix + ./ports.nix + ]; + + networking.firewall.logRefusedConnections = lib.mkDefault false; + + networking.nftables.enable = true; +} diff --git a/modules/networking/network-manager.nix b/modules/networking/network-manager.nix new file mode 100644 index 0000000..d70a218 --- /dev/null +++ b/modules/networking/network-manager.nix @@ -0,0 +1,47 @@ +{ config, lib, ... }: + +lib.mkIf config.jalr.gui.enable { + programs.nm-applet = { + enable = true; + indicator = true; + }; + + networking.networkmanager = { + enable = true; + ensureProfiles.profiles = { + "38C3" = { + connection = { + id = "38C3"; + type = "wifi"; + }; + wifi = { + mode = "infrastructure"; + ssid = "38C3"; + }; + wifi-security = { + auth-alg = "open"; + key-mgmt = "wpa-eap"; + }; + "802-1x" = { + anonymous-identity = "38C3"; + eap = "ttls;"; + identity = "38C3"; + password = "38C3"; + phase2-auth = "pap"; + altsubject-matches = "DNS:radius.c3noc.net"; + ca-cert = "${builtins.fetchurl { + url = "https://letsencrypt.org/certs/isrgrootx1.pem"; + sha256 = "sha256:1la36n2f31j9s03v847ig6ny9lr875q3g7smnq33dcsmf2i5gd92"; + }}"; + }; + ipv4 = { + method = "auto"; + }; + ipv6 = { + addr-gen-mode = "default"; + method = "auto"; + }; + }; + }; + }; +} diff --git a/modules/networking/ports.nix b/modules/networking/ports.nix new file mode 100644 index 0000000..3e656aa --- /dev/null +++ b/modules/networking/ports.nix @@ -0,0 +1,20 @@ +{ lib, ... }: + +{ + options.networking.ports = with lib; with lib.types; mkOption { + type = attrsOf (types.submodule { + options = { + tcp = mkOption { + type = oneOf [ port (listOf port) (attrsOf port) ]; + description = "TCP ports"; + default = [ ]; + }; + udp = mkOption { + type = oneOf [ port (listOf port) (attrsOf port) ]; + description = "UDP ports"; + default = [ ]; + }; + }; + }); + }; +} diff --git a/modules/nix.nix b/modules/nix.nix index bb4a441..eb8f9b4 100644 --- a/modules/nix.nix +++ b/modules/nix.nix @@ -1,12 +1,7 @@ -{ config, lib, pkgs, inputs, system, ... }: +{ lib, pkgs, inputs, system, ... }: { nix = { - package = pkgs.nixFlakes; - extraOptions = '' - experimental-features = nix-command flakes - ''; - daemonCPUSchedPolicy = "idle"; daemonIOSchedClass = "idle"; daemonIOSchedPriority = 7; @@ -16,37 +11,47 @@ ]; settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; trusted-users = [ "@wheel" ]; auto-optimise-store = true; allowed-users = [ "@wheel" ]; + + log-lines = lib.mkDefault 25; + + # Avoid disk full issues + max-free = lib.mkDefault (3000 * 1024 * 1024); + min-free = lib.mkDefault (512 * 1024 * 10); + + download-buffer-size = lib.mkDefault (512 * 1024 * 1024); + }; + gc = { + automatic = true; + options = "--delete-older-than 30d"; + randomizedDelaySec = "60 min"; }; }; - nixpkgs.overlays = with inputs; [ - self.overlay - (final: prev: { + systemd.services.nix-daemon.serviceConfig.OOMScoreAdjust = 250; + + nixpkgs.config.permittedInsecurePackages = [ + "olm-3.2.16" + ]; + + nixpkgs.overlays = [ + inputs.self.overlays.default + (_: prev: { master = import inputs.nixpkgsMaster { inherit system; - config = prev.config; + inherit (prev) config; }; }) - ] - # Tradebyte access points use legacy crypto - ++ lib.optional config.jalr.tradebyte.enable ( - final: prev: - let - inherit (prev) callPackage; - in - { - wpa_supplicant = prev.wpa_supplicant.overrideAttrs (attrs: { - patches = attrs.patches ++ [ - ./wpa_supplicant/SSL_CTX_set_options-SSL_OP_LEGACY_SERVER_CONNECT.patch - ]; - }); - } - ); + ]; environment.systemPackages = with pkgs; [ cached-nix-shell + git ]; } diff --git a/modules/obs.nix b/modules/obs.nix index 7f66004..e8bcf9f 100644 --- a/modules/obs.nix +++ b/modules/obs.nix @@ -11,8 +11,4 @@ lib.mkIf config.jalr.gui.enable { environment.systemPackages = with pkgs; [ v4l-utils ]; - - services.udev.extraRules = '' - SUBSYSTEM=="video4linux", ATTR{idVendor}=="2109", ATTR{idProduct}=="2813", SYMLINK+="video_c922" - ''; } diff --git a/modules/pipewire.nix b/modules/pipewire.nix index cb4cf02..ea97fb6 100644 --- a/modules/pipewire.nix +++ b/modules/pipewire.nix @@ -1,14 +1,14 @@ { config, lib, pkgs, ... }: lib.mkIf config.jalr.gui.enable { - sound.enable = true; - hardware.pulseaudio.enable = false; + services.pulseaudio.enable = false; # FIXME #hardware.pulseaudio.extraModules = [ pkgs.pulseaudio-modules-bt ]; services.pipewire = { enable = true; + package = pkgs.master.pipewire; pulse = { enable = true; }; @@ -19,6 +19,33 @@ lib.mkIf config.jalr.gui.enable { enable = true; support32Bit = true; }; + extraConfig = { + pipewire-pulse."10-snapcast-discover" = { + "context.modules" = [ + { + name = "libpipewire-module-snapcast-discover"; + args = { + stream.rules = [{ + matches = [{ + snapcast.ip = "~.*"; + }]; + actions = { + create-stream = { }; + }; + }]; + }; + } + ]; + }; + /* + pipewire."raop-sink" = { + "context.modules" = [ + { name = "libpipewire-module-raop-discover"; args = { }; } + ]; + }; + */ + }; + raopOpenFirewall = true; }; environment.systemPackages = with pkgs; [ @@ -35,4 +62,7 @@ lib.mkIf config.jalr.gui.enable { value = "unlimited"; } ]; + + security.rtkit.enable = true; + } diff --git a/modules/podman.nix b/modules/podman.nix index d7453a9..79486f1 100644 --- a/modules/podman.nix +++ b/modules/podman.nix @@ -1,7 +1,11 @@ +{ pkgs, ... }: { virtualisation.podman = { enable = true; dockerCompat = true; dockerSocket.enable = true; }; + environment.systemPackages = with pkgs; [ + podman-compose + ]; } diff --git a/modules/printers/default.nix b/modules/printers/default.nix index a29723f..b207377 100644 --- a/modules/printers/default.nix +++ b/modules/printers/default.nix @@ -1,7 +1,11 @@ +{ config, lib, ... }: + { imports = [ ./hl3172cdw.nix - ./p-touch_p700.nix ]; + config = lib.mkIf config.jalr.gui.enable { + # install virtual pdf printer + services.printing.cups-pdf.enable = true; + }; } - diff --git a/modules/printers/p-touch_p700.nix b/modules/printers/p-touch_p700.nix deleted file mode 100644 index 627ad56..0000000 --- a/modules/printers/p-touch_p700.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, lib, ... }: - -{ - services.udev.extraRules = '' - SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2061", OWNER="root", GROUP="users", MODE="660" - ''; -} diff --git a/modules/providers/hetzner-cloud.nix b/modules/providers/hetzner-cloud.nix new file mode 100644 index 0000000..c1e0b16 --- /dev/null +++ b/modules/providers/hetzner-cloud.nix @@ -0,0 +1,108 @@ +{ config +, pkgs +, modulesPath +, ... +}: +let + cfg = config.disko; +in +{ + imports = [ + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + config = { + networking.useDHCP = false; + + systemd.network = { + enable = true; + networks."10-wan" = { + matchConfig.Name = "enp1s0"; + networkConfig.DHCP = "ipv4"; + routes = [ + { Gateway = "fe80::1"; } + ]; + }; + }; + + boot = { + loader = { + grub.enable = pkgs.hostPlatform.system == "x86_64-linux"; + systemd-boot = { + enable = pkgs.hostPlatform.system == "aarch64-linux"; + configurationLimit = 10; + }; + efi = { + efiSysMountPoint = "/boot"; + canTouchEfiVariables = true; + }; + }; + }; + disko.devices = { + disk = { + virt = { + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + size = "1M"; + type = "EF02"; # for grub MBR + priority = 1; # Needs to be first partition + }; + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + linux = { + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + postCreateHook = + let + inherit (cfg.devices.disk.virt.content.partitions.linux) device; + in + '' + mountpoint="$(mktemp -d)" + mount "${device}" "$mountpoint" -o subvol=/ + trap 'umount "$mountpoint"; rmdir "$mountpoint"' EXIT + btrfs subvolume snapshot -r $mountpoint/root $mountpoint/root-blank + ''; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" ]; + }; + "/log" = { + mountpoint = "/var/log"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/persist" = { + mountpoint = "/persist"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/qbittorrent/default.nix b/modules/qbittorrent/default.nix deleted file mode 100644 index 810685b..0000000 --- a/modules/qbittorrent/default.nix +++ /dev/null @@ -1,94 +0,0 @@ -{ config, inputs, lib, pkgs, ... }: -let - cfg = config.jalr.qbittorrent; -in -{ - options.jalr.qbittorrent = { - enable = lib.mkEnableOption "the qbittorrent service"; - homeDir = lib.mkOption { - type = lib.types.path; - default = "/var/lib/qbittorrent"; - }; - configDir = lib.mkOption { - type = lib.types.path; - default = "${cfg.homeDir}/config"; - }; - downloadDir = lib.mkOption { - type = lib.types.path; - default = "${cfg.homeDir}/download"; - }; - webuiPort = lib.mkOption { - type = lib.types.int; - default = 8099; - }; - sopsFile = lib.mkOption { - type = lib.types.path; - default = ../../hosts/${config.networking.hostName}/secrets.yaml; - description = '' - The sops secret file that includes the htpasswd file. - ''; - }; - fqdn = lib.mkOption { - type = lib.types.str; - description = "The fqdn nginx should listen on. It must not be used for anything else."; - }; - }; - - config = lib.mkIf cfg.enable - { - users.users.qbittorrent = { - group = "qbittorrent"; - home = cfg.homeDir; - isSystemUser = true; - }; - users.groups.qbittorrent = { }; - - systemd.tmpfiles.rules = [ - "d '${cfg.downloadDir}' 0775 qbittorrent users - -" - "d '${cfg.homeDir}' 0771 qbittorrent qbittorrent - -" - ]; - - sops.secrets.sturzbach-htpasswd = { - sopsFile = cfg.sopsFile; - owner = "nginx"; - }; - - systemd.services.qbittorrent = { - description = "qBittorrent Service"; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Restart = "always"; - ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox --profile=${cfg.configDir} --webui-port=${toString cfg.webuiPort}"; - User = "qbittorrent"; - Group = "qbittorrent"; - - # Increase number of open file descriptors (default: 1024) - LimitNOFILE = 65536; - - # systemd-analyze --no-pager security qbittorrent.service - CapabilityBoundingSet = null; - PrivateDevices = true; - PrivateTmp = true; - PrivateUsers = true; - ProtectHome = true; - RestrictNamespaces = true; - SystemCallFilter = "@system-service"; - }; - }; - - services.nginx.virtualHosts."${cfg.fqdn}" = { - enableACME = lib.mkDefault true; - forceSSL = lib.mkDefault true; - - basicAuthFile = config.sops.secrets.sturzbach-htpasswd.path; - - locations = { - "/" = { - proxyPass = "http://127.0.0.1:${toString cfg.webuiPort}"; - proxyWebsockets = true; - }; - }; - }; - }; -} diff --git a/modules/remarkable.nix b/modules/remarkable.nix new file mode 100644 index 0000000..bce495c --- /dev/null +++ b/modules/remarkable.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: +lib.mkIf config.jalr.gui.enable { + # allow port for reMarkable screen sharing + networking.firewall.allowedUDPPorts = [ 5901 ]; + + #services.printing.cups-pdf.instances.remarkable = { + # settings.Out = "socket://remarkable" + #}; + + # https://github.com/Evidlo/remarkable_printer needs to be + # installed on the reMarkable + hardware.printers.ensurePrinters = [ + { + name = "remarkable"; + description = "reMarkable virtual printer"; + deviceUri = "socket://remarkable"; + #location = "/var/spool/cups-pdf-pdf/users/\${USER}"; + model = "CUPS-PDF_opt.ppd"; + ppdOptions = { }; + } + ]; +} diff --git a/modules/sdr.nix b/modules/sdr.nix deleted file mode 100644 index a0b7686..0000000 --- a/modules/sdr.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.jalr; -in -{ - options.jalr = { - sdr = { - enable = pkgs.lib.mkEnableOption "Enable software defined radio"; - }; - }; - config = lib.mkIf cfg.sdr.enable { - services.udev.extraRules = '' - # rad10 - SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="cc15", GROUP="users", MODE="0660" - SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6089", GROUP="users", MODE="0660" - ''; - }; -} diff --git a/modules/sshd.nix b/modules/sshd.nix index 3ad95f3..040ed09 100644 --- a/modules/sshd.nix +++ b/modules/sshd.nix @@ -1,6 +1,61 @@ +{ lib +, config +, ... +}: + { services.openssh = { enable = true; - settings.PasswordAuthentication = false; + settings = { + KbdInteractiveAuthentication = false; + Ciphers = [ + "aes256-gcm@openssh.com" + ]; + # Use key exchange algorithms recommended by `nixpkgs#ssh-audit` + KexAlgorithms = [ + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + "sntrup761x25519-sha512@openssh.com" + ]; + PasswordAuthentication = false; + StreamLocalBindUnlink = true; # unbind gnupg sockets if they exists + UseDns = false; + X11Forwarding = false; + }; + hostKeys = [ + { + path = "/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + ]; + authorizedKeysFiles = lib.mkForce [ "/etc/ssh/authorized_keys.d/%u" ]; }; + + networking.nftables.tables."nixos-fw".content = lib.mkOrder 20 '' + set ssh-ratelimit-v4 { + type ipv4_addr + timeout 60s + flags dynamic + } + + set ssh-ratelimit-v6 { + type ipv6_addr + timeout 60s + flags dynamic + } + ''; + + # Implement connection rate limit + services.openssh.openFirewall = false; + networking.firewall.extraInputRules = lib.mkOrder 5 ( + let + ports = builtins.concatStringsSep ", " (map builtins.toString config.services.openssh.ports); + in + '' + tcp dport { ${ports} } update @ssh-ratelimit-v4 { ip saddr limit rate 1/second burst 10 packets } accept + tcp dport { ${ports} } update @ssh-ratelimit-v6 { ip6 saddr limit rate 1/second burst 10 packets } accept + '' + ); } diff --git a/modules/steelseries-nova-pro.nix b/modules/steelseries-nova-pro.nix new file mode 100644 index 0000000..d432dca --- /dev/null +++ b/modules/steelseries-nova-pro.nix @@ -0,0 +1,7 @@ +{ lib, config, ... }: + +lib.mkIf config.jalr.gui.enable { + services.gg-chatmix = { + enable = true; + }; +} diff --git a/modules/sudo.nix b/modules/sudo.nix index ebe10d1..da6577a 100644 --- a/modules/sudo.nix +++ b/modules/sudo.nix @@ -1,5 +1,21 @@ -{ pkgs, inputs, ... }: - +let + commandsWithoutPassword = [ + "/run/current-system/sw/bin/systemctl restart tor.service" + ]; +in { - security.sudo.execWheelOnly = true; + security.sudo = { + execWheelOnly = true; + extraRules = [ + { + groups = [ "wheel" ]; + commands = map + (cmd: { + command = cmd; + options = [ "NOPASSWD" ]; + }) + commandsWithoutPassword; + } + ]; + }; } diff --git a/modules/sway.nix b/modules/sway.nix index c9c0e5c..dbd37fe 100644 --- a/modules/sway.nix +++ b/modules/sway.nix @@ -1,33 +1,65 @@ { config, lib, pkgs, ... }: -lib.mkIf config.jalr.gui.enable { - programs.sway = { - enable = true; - # FIXME: move to home manager - extraPackages = with pkgs; [ - grim - mako - gammastep - slurp - wl-clipboard - xwayland - ]; - extraSessionCommands = '' - export XKB_DEFAULT_LAYOUT=de # TODO: test if we need it - export XKB_DEFAULT_VARIANT=neo # TODO: test if we need it - export QT_QPA_PLATFORM=wayland - export QT_WAYLAND_DISABLE_WINDOWDECORATION=1 - export ELM_ENGINE=wayland_shm - export GDK_BACKEND=wayland - export _JAVA_AWT_WM_NONREPARENTING=1 - ''; +lib.mkIf (config.jalr.gui.enable && config.jalr.gui.sway.enable) { + services.displayManager.sessionPackages = [ pkgs.sway ]; + + programs = { + wshowkeys.enable = true; + dconf.enable = true; + sway = { + enable = true; + xwayland.enable = true; + wrapperFeatures.gtk = true; + }; }; + services.greetd = { + enable = !config.jalr.gui.gnome.enable; + settings = + let + command = pkgs.writeShellScript "sway-init" '' + systemctl --user import-environment PATH + #systemctl --user restart xdg-desktop-portal.service + exec ${pkgs.sway}/bin/sway + ''; + user = "jalr"; + in + { + default_session = { + inherit command; + inherit user; + }; + initial_session = { + inherit command; + inherit user; + }; + }; + }; + + hardware.graphics.enable = true; + + security.polkit.enable = true; + + security.pam.loginLimits = [{ + domain = "@users"; + item = "rtprio"; + type = "-"; + value = 1; + }]; + xdg = { + icons.enable = true; portal = { enable = true; - extraPortals = [ pkgs.xdg-desktop-portal-wlr ]; + wlr.enable = true; + extraPortals = [ + pkgs.xdg-desktop-portal-gtk + ]; + xdgOpenUsePortal = true; }; - icons.enable = true; }; + + environment.systemPackages = with pkgs; [ + adwaita-icon-theme + ]; } diff --git a/modules/tor.nix b/modules/tor.nix deleted file mode 100644 index 26b357b..0000000 --- a/modules/tor.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - services.tor = { - enable = true; - settings = { - DNSPort = 9053; - AutomapHostsOnResolve = true; - AutomapHostsSuffixes = [ - ".exit" - ".onion" - ]; - }; - }; -} diff --git a/modules/tradebyte/default.nix b/modules/tradebyte/default.nix deleted file mode 100644 index de83fba..0000000 --- a/modules/tradebyte/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ lib, ... }: - -{ - options.jalr = { - tradebyte.enable = lib.mkEnableOption "TB.config"; - }; -} - diff --git a/modules/udev.nix b/modules/udev.nix new file mode 100644 index 0000000..908a711 --- /dev/null +++ b/modules/udev.nix @@ -0,0 +1,31 @@ +let + usbDeviceRules = [ + # rad10 + { vendor = "1d50"; product = "cc15"; group = "users"; mode = "0660"; } + { vendor = "1d50"; product = "6089"; group = "users"; mode = "0660"; } + + # DJI Goggles + { vendor = "2ca3"; product = "001f"; group = "plugdev"; mode = "0660"; } + + # uDMX + { vendor = "16c0"; product = "05dc"; group = "users"; mode = "0660"; } + + # Brother P-touch P700 + { vendor = "04f9"; product = "2061"; group = "users"; mode = "0660"; } + + # RP2040 in BOOTSEL mode + { vendor = "2e8a"; product = "0003"; group = "users"; mode = "0660"; } + + # RP2350 in BOOTSEL mode + { vendor = "2e8a"; product = "000f"; group = "users"; mode = "0660"; } + + # WCH Link (CMSIS-DAP compatible adapter) + { vendor = "1a86"; product = "8010"; group = "plugdev"; mode = "0660"; } + ]; + + mkUsbRule = rule: + ''SUBSYSTEM=="usb", ATTR{idVendor}=="${rule.vendor}", ATTR{idProduct}=="${rule.product}", GROUP="${rule.group}", MODE="${rule.mode}"''; +in +{ + services.udev.extraRules = builtins.concatStringsSep "\n" (map mkUsbRule usbDeviceRules); +} diff --git a/modules/unfree.nix b/modules/unfree.nix index 3394261..a24df1c 100644 --- a/modules/unfree.nix +++ b/modules/unfree.nix @@ -1,8 +1,11 @@ { lib, ... }: { - nixpkgs.config.allowUnfreePredicate = (pkg: lib.elem (lib.getName pkg) [ - "unifi-controller" + nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [ "mongodb" - ]); + "rar2fs" + "roomeqwizard" + "unifi-controller" + "unrar" + ]; } diff --git a/modules/upgrade-diff.nix b/modules/upgrade-diff.nix new file mode 100644 index 0000000..ccdbe4c --- /dev/null +++ b/modules/upgrade-diff.nix @@ -0,0 +1,14 @@ +# MIT Jörg Thalheim - https://github.com/Mic92/dotfiles/blob/c6cad4e57016945c4816c8ec6f0a94daaa0c3203/nixos/modules/upgrade-diff.nix +{ config, pkgs, ... }: +{ + system.activationScripts.diff = { + supportsDryActivation = true; + text = '' + if [[ -e /run/current-system ]]; then + echo "--- diff to current-system" + ${pkgs.nvd}/bin/nvd --nix-bin-dir=${config.nix.package}/bin diff /run/current-system "$systemConfig" + echo "---" + fi + ''; + }; +} diff --git a/modules/wireshark.nix b/modules/wireshark.nix deleted file mode 100644 index 878f649..0000000 --- a/modules/wireshark.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, lib, pkgs, ... }: -lib.mkIf config.jalr.gui.enable { - programs.wireshark = { - enable = true; - package = pkgs.wireshark; - }; -} diff --git a/modules/wireshark/default.nix b/modules/wireshark/default.nix new file mode 100644 index 0000000..6c1b0c0 --- /dev/null +++ b/modules/wireshark/default.nix @@ -0,0 +1,23 @@ +{ config, lib, pkgs, ... }: +let + extcap = ./extcap; + pythonWithPackages = pkgs.python3.withPackages (pp: with pp; [ + pyserial + psutil + ]); + nrf_sniffer_ble = pkgs.writeShellScript "nrf_sniffer_ble" '' + script_path=$(dirname `which $0`) + + exec ${pythonWithPackages}/bin/python3 $script_path/nrf_sniffer_ble.py "$@" + ''; +in +lib.mkIf config.jalr.gui.enable { + programs.wireshark = { + enable = true; + package = pkgs.wireshark.overrideAttrs (o: { + postInstall = '' + cp -r ${extcap}/* ${nrf_sniffer_ble} $out/lib/wireshark/extcap + '' + o.postInstall; + }); + }; +} diff --git a/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py b/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py new file mode 100644 index 0000000..8c218e5 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py @@ -0,0 +1,91 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import time, os, logging +from . import Logger +from . import Pcap + + +DEFAULT_CAPTURE_FILE_DIR = Logger.DEFAULT_LOG_FILE_DIR +DEFAULT_CAPTURE_FILE_NAME = "capture.pcap" + + +def get_capture_file_path(capture_file_path=None): + default_path = os.path.join(DEFAULT_CAPTURE_FILE_DIR, DEFAULT_CAPTURE_FILE_NAME) + if capture_file_path is None: + return default_path + if os.path.splitext(capture_file_path)[1] != ".pcap": + return default_path + return os.path.abspath(capture_file_path) + + +class CaptureFileHandler: + def __init__(self, capture_file_path=None, clear=False): + filename = get_capture_file_path(capture_file_path) + if not os.path.isdir(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + self.filename = filename + self.backupFilename = self.filename + ".1" + if not os.path.isfile(self.filename): + self.startNewFile() + elif os.path.getsize(self.filename) > 20000000: + self.doRollover() + if clear: + # clear file + self.startNewFile() + + def startNewFile(self): + with open(self.filename, "wb") as f: + f.write(Pcap.get_global_header()) + + def doRollover(self): + try: + os.remove(self.backupFilename) + except: + logging.exception("capture file rollover remove backup failed") + try: + os.rename(self.filename, self.backupFilename) + self.startNewFile() + except: + logging.exception("capture file rollover failed") + + def writePacket(self, packet): + with open(self.filename, "ab") as f: + packet = Pcap.create_packet( + bytes([packet.boardId] + packet.getList()), packet.time + ) + f.write(packet) diff --git a/modules/wireshark/extcap/SnifferAPI/Devices.py b/modules/wireshark/extcap/SnifferAPI/Devices.py new file mode 100644 index 0000000..61ac961 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Devices.py @@ -0,0 +1,150 @@ +# Copyright (c) 2017, Nordic Semiconductor ASA +# +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import Notifications +import logging, threading + + +class DeviceList(Notifications.Notifier): + def __init__(self, *args, **kwargs): + Notifications.Notifier.__init__(self, *args, **kwargs) + logging.info("args: " + str(args)) + logging.info("kwargs: " + str(kwargs)) + self._deviceListLock = threading.RLock() + with self._deviceListLock: + self.devices = [] + + def __len__(self): + return len(self.devices) + + def __repr__(self): + return "Sniffer Device List: " + str(self.asList()) + + def clear(self): + logging.info("Clearing") + with self._deviceListLock: + self.devices = [] + self.notify("DEVICES_CLEARED") + + def appendOrUpdate(self, newDevice): + with self._deviceListLock: + existingDevice = self.find(newDevice) + + # Add device to the list of devices being displayed, but only if CRC is OK + if existingDevice == None: + self.append(newDevice) + else: + updated = False + if (newDevice.name != '""') and (existingDevice.name == '""'): + existingDevice.name = newDevice.name + updated = True + + if ( + newDevice.RSSI != 0 + and (existingDevice.RSSI < (newDevice.RSSI - 5)) + or (existingDevice.RSSI > (newDevice.RSSI + 2)) + ): + existingDevice.RSSI = newDevice.RSSI + updated = True + + if updated: + self.notify("DEVICE_UPDATED", existingDevice) + + def append(self, device): + self.devices.append(device) + self.notify("DEVICE_ADDED", device) + + def find(self, id): + if type(id) == list: + for dev in self.devices: + if dev.address == id: + return dev + elif type(id) == int: + return self.devices[id] + elif type(id) == str: + for dev in self.devices: + if dev.name in [id, '"' + id + '"']: + return dev + elif id.__class__.__name__ == "Device": + return self.find(id.address) + return None + + def remove(self, id): + if type(id) == list: # address + device = self.devices.pop(self.devices.index(self.find(id))) + elif type(id) == int: + device = self.devices.pop(id) + elif type(id) == Device: + device = self.devices.pop(self.devices.index(self.find(id.address))) + self.notify("DEVICE_REMOVED", device) + + def index(self, device): + index = 0 + for dev in self.devices: + if dev.address == device.address: + return index + index += 1 + return None + + def setFollowed(self, device): + if device in self.devices: + for dev in self.devices: + dev.followed = False + device.followed = True + self.notify("DEVICE_FOLLOWED", device) + + def asList(self): + return self.devices[:] + + +class Device: + def __init__(self, address, name, RSSI): + self.address = address + self.name = name + self.RSSI = RSSI + self.followed = False + + def __repr__(self): + return 'Bluetooth LE device "' + self.name + '" (' + str(self.address) + ")" + + +def listToString(list): + str = "" + for i in list: + str += chr(i) + return str diff --git a/modules/wireshark/extcap/SnifferAPI/Exceptions.py b/modules/wireshark/extcap/SnifferAPI/Exceptions.py new file mode 100644 index 0000000..86f356a --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Exceptions.py @@ -0,0 +1,66 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +class SnifferTimeout(Exception): + pass + + +class UARTPacketError(Exception): + pass + + +class LockedException(Exception): + def __init__(self, message): + self.message = message + + +class InvalidPacketException(Exception): + pass + + +class InvalidAdvChannel(Exception): + pass + + +# Internal Use +class SnifferWatchDogTimeout(SnifferTimeout): + pass + + +# Internal Use +class ExitCodeException(Exception): + pass diff --git a/modules/wireshark/extcap/SnifferAPI/Filelock.py b/modules/wireshark/extcap/SnifferAPI/Filelock.py new file mode 100644 index 0000000..7bf21b5 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Filelock.py @@ -0,0 +1,67 @@ +import os +import logging +from sys import platform + +if platform == "linux": + import psutil + +from . import Exceptions + +# Lock file management. +# ref: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s09.html +# +# Stored in /var/lock: +# The naming convention which must be used is "LCK.." followed by the base name of the device. +# For example, to lock /dev/ttyS0 the file "LCK..ttyS0" would be created. +# HDB UUCP lock file format: +# process identifier (PID) as a ten byte ASCII decimal number, with a trailing newline + + +def lockpid(lockfile): + if os.path.isfile(lockfile): + with open(lockfile) as fd: + lockpid = fd.read() + + try: + return int(lockpid) + except: + logging.info("Lockfile is invalid. Overriding it..") + os.remove(lockfile) + return 0 + + return 0 + + +def lock(port): + if platform != "linux": + return + + tty = os.path.basename(port) + lockfile = os.path.join("/run", "user", f"{os.getuid()}", f"{tty}.lock") + + lockedpid = lockpid(lockfile) + if lockedpid: + if lockedpid == os.getpid(): + return + + if psutil.pid_exists(lockedpid): + raise Exceptions.LockedException(f"Device {port} is locked") + else: + logging.info("Lockfile is stale. Overriding it..") + os.remove(lockfile) + + fd = open(lockfile, "w") + with open(lockfile, "w") as fd: + fd.write(f"{os.getpid():10}") + + +def unlock(port): + if platform != "linux": + return + + tty = os.path.basename(port) + lockfile = f"/var/lock/LCK..{tty}" + + lockedpid = lockpid(lockfile) + if lockedpid == os.getpid(): + os.remove(lockfile) diff --git a/modules/wireshark/extcap/SnifferAPI/Logger.py b/modules/wireshark/extcap/SnifferAPI/Logger.py new file mode 100644 index 0000000..228a0f1 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Logger.py @@ -0,0 +1,214 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import time, os, logging, traceback, threading +import logging.handlers as logHandlers + +################################################################# +# This file contains the logger. To log a line, simply write # +# 'logging.[level]("whatever you want to log")' # +# [level] is one of {info, debug, warning, error, critical, # +# exception} # +# See python logging documentation # +# As long as Logger.initLogger has been called beforehand, this # +# will result in the line being appended to the log file # +################################################################# + +appdata = os.getenv("appdata") +if appdata: + DEFAULT_LOG_FILE_DIR = os.path.join( + appdata, "Nordic Semiconductor", "Sniffer", "logs" + ) +else: + DEFAULT_LOG_FILE_DIR = "/tmp/logs" + +DEFAULT_LOG_FILE_NAME = "log.txt" + +logFileName = None +logHandler = None +logHandlerArray = [] +logFlusher = None + +myMaxBytes = 1000000 + + +def setLogFileName(log_file_path): + global logFileName + logFileName = os.path.abspath(log_file_path) + + +# Ensure that the directory we are writing the log file to exists. +# Create our logfile, and write the timestamp in the first line. +def initLogger(): + try: + global logFileName + if logFileName is None: + logFileName = os.path.join(DEFAULT_LOG_FILE_DIR, DEFAULT_LOG_FILE_NAME) + + # First, make sure that the directory exists + if not os.path.isdir(os.path.dirname(logFileName)): + os.makedirs(os.path.dirname(logFileName)) + + # If the file does not exist, create it, and save the timestamp + if not os.path.isfile(logFileName): + with open(logFileName, "w") as f: + f.write(str(time.time()) + str(os.linesep)) + + global logFlusher + global logHandlerArray + + logHandler = MyRotatingFileHandler( + logFileName, mode="a", maxBytes=myMaxBytes, backupCount=3 + ) + logFormatter = logging.Formatter( + "%(asctime)s %(levelname)s: %(message)s", datefmt="%d-%b-%Y %H:%M:%S (%z)" + ) + logHandler.setFormatter(logFormatter) + logger = logging.getLogger() + logger.addHandler(logHandler) + logger.setLevel(logging.INFO) + logFlusher = LogFlusher(logHandler) + logHandlerArray.append(logHandler) + except: + print("LOGGING FAILED") + print(traceback.format_exc()) + raise + + +def shutdownLogger(): + if logFlusher is not None: + logFlusher.stop() + logging.shutdown() + + +# Clear the log (typically after it has been sent on email) +def clearLog(): + try: + logHandler.doRollover() + except: + print("LOGGING FAILED") + raise + + +# Returns the timestamp residing on the first line of the logfile. Used for checking the time of creation +def getTimestamp(): + try: + with open(logFileName, "r") as f: + f.seek(0) + return f.readline() + except: + print("LOGGING FAILED") + + +def addTimestamp(): + try: + with open(logFileName, "a") as f: + f.write(str(time.time()) + os.linesep) + except: + print("LOGGING FAILED") + + +# Returns the entire content of the logfile. Used when sending emails +def readAll(): + try: + text = "" + with open(logFileName, "r") as f: + text = f.read() + return text + except: + print("LOGGING FAILED") + + +def addLogHandler(logHandler): + global logHandlerArray + logger = logging.getLogger() + logger.addHandler(logHandler) + logger.setLevel(logging.INFO) + logHandlerArray.append(logHandler) + + +def removeLogHandler(logHandler): + global logHandlerArray + logger = logging.getLogger() + logger.removeHandler(logHandler) + logHandlerArray.remove(logHandler) + + +class MyRotatingFileHandler(logHandlers.RotatingFileHandler): + def doRollover(self): + try: + logHandlers.RotatingFileHandler.doRollover(self) + addTimestamp() + self.maxBytes = myMaxBytes + except: + # There have been permissions issues with the log files. + self.maxBytes += int(myMaxBytes / 2) + + +class LogFlusher(threading.Thread): + def __init__(self, logHandler): + threading.Thread.__init__(self) + + self.daemon = True + self.handler = logHandler + self.exit = threading.Event() + + self.start() + + def run(self): + while True: + if self.exit.wait(10): + try: + self.doFlush() + except AttributeError as e: + print(e) + break + self.doFlush() + + def doFlush(self): + self.handler.flush() + os.fsync(self.handler.stream.fileno()) + + def stop(self): + self.exit.set() + + +if __name__ == "__main__": + initLogger() + for i in range(50): + logging.info("test log no. " + str(i)) + print("test log no. ", i) diff --git a/modules/wireshark/extcap/SnifferAPI/Notifications.py b/modules/wireshark/extcap/SnifferAPI/Notifications.py new file mode 100644 index 0000000..b7cba37 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Notifications.py @@ -0,0 +1,92 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import threading, logging + + +class Notification: + def __init__(self, key, msg=None): + if type(key) is not str: + raise TypeError("Invalid notification key: " + str(key)) + self.key = key + self.msg = msg + + def __repr__(self): + return "Notification (key: %s, msg: %s)" % (str(self.key), str(self.msg)) + + +class Notifier: + def __init__(self, callbacks=[], **kwargs): + self.callbacks = {} + self.callbackLock = threading.RLock() + + for callback in callbacks: + self.subscribe(*callback) + + def clearCallbacks(self): + with self.callbackLock: + self.callbacks.clear() + + def subscribe(self, key, callback): + with self.callbackLock: + if callback not in self.getCallbacks(key): + self.getCallbacks(key).append(callback) + + def unSubscribe(self, key, callback): + with self.callbackLock: + if callback in self.getCallbacks(key): + self.getCallbacks(key).remove(callback) + + def getCallbacks(self, key): + with self.callbackLock: + if key not in self.callbacks: + self.callbacks[key] = [] + return self.callbacks[key] + + def notify(self, key=None, msg=None, notification=None): + with self.callbackLock: + if notification == None: + notification = Notification(key, msg) + + for callback in self.getCallbacks(notification.key): + callback(notification) + + for callback in self.getCallbacks("*"): + callback(notification) + + def passOnNotification(self, notification): + self.notify(notification=notification) diff --git a/modules/wireshark/extcap/SnifferAPI/Packet.py b/modules/wireshark/extcap/SnifferAPI/Packet.py new file mode 100644 index 0000000..bc4abd9 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Packet.py @@ -0,0 +1,651 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import UART, Exceptions, Notifications +import time, logging, os, sys, serial +from .Types import * + +ADV_ACCESS_ADDRESS = [0xD6, 0xBE, 0x89, 0x8E] + +SYNCWORD_POS = 0 +PAYLOAD_LEN_POS_V1 = 1 +PAYLOAD_LEN_POS = 0 +PROTOVER_POS = PAYLOAD_LEN_POS + 2 +PACKETCOUNTER_POS = PROTOVER_POS + 1 +ID_POS = PACKETCOUNTER_POS + 2 + +BLE_HEADER_LEN_POS = ID_POS + 1 +FLAGS_POS = BLE_HEADER_LEN_POS + 1 +CHANNEL_POS = FLAGS_POS + 1 +RSSI_POS = CHANNEL_POS + 1 +EVENTCOUNTER_POS = RSSI_POS + 1 +TIMESTAMP_POS = EVENTCOUNTER_POS + 2 +BLEPACKET_POS = TIMESTAMP_POS + 4 +TXADD_POS = BLEPACKET_POS + 4 +TXADD_MSK = 0x40 +PAYLOAD_POS = BLE_HEADER_LEN_POS + +HEADER_LENGTH = 6 +BLE_HEADER_LENGTH = 10 + +VALID_ADV_CHANS = [37, 38, 39] + +PACKET_COUNTER_CAP = 2**16 + + +class PacketReader(Notifications.Notifier): + def __init__(self, portnum=None, callbacks=[], baudrate=None): + Notifications.Notifier.__init__(self, callbacks) + self.portnum = portnum + try: + self.uart = UART.Uart(portnum, baudrate) + except serial.SerialException as e: + logging.exception("Error opening UART %s" % str(e)) + self.uart = UART.Uart() + self.packetCounter = 0 + self.lastReceivedPacketCounter = 0 + self.lastReceivedPacket = None + self.lastReceivedTimestampPacket = None + self.supportedProtocolVersion = PROTOVER_V3 + + def setup(self): + pass + + def doExit(self): + # This method will always join the Uart worker thread + self.uart.close() + # Clear method references to avoid uncollectable cyclic references + self.clearCallbacks() + + # This function takes a byte list, encode it in SLIP protocol and return the encoded byte list + def encodeToSLIP(self, byteList): + tempSLIPBuffer = [] + tempSLIPBuffer.append(SLIP_START) + for i in byteList: + if i == SLIP_START: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_START) + elif i == SLIP_END: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_END) + elif i == SLIP_ESC: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_ESC) + else: + tempSLIPBuffer.append(i) + tempSLIPBuffer.append(SLIP_END) + return tempSLIPBuffer + + # This function uses getSerialByte() function to get SLIP encoded bytes from the serial port and return a decoded byte list + # Based on https://github.com/mehdix/pyslip/ + def decodeFromSLIP(self, timeout=None, complete_timeout=None): + dataBuffer = [] + startOfPacket = False + endOfPacket = False + + if complete_timeout is not None: + time_start = time.time() + + while not startOfPacket and ( + complete_timeout is None or (time.time() - time_start < complete_timeout) + ): + res = self.getSerialByte(timeout) + startOfPacket = res == SLIP_START + + while not endOfPacket and ( + complete_timeout is None or (time.time() - time_start < complete_timeout) + ): + serialByte = self.getSerialByte(timeout) + if serialByte == SLIP_END: + endOfPacket = True + elif serialByte == SLIP_ESC: + serialByte = self.getSerialByte(timeout) + if serialByte == SLIP_ESC_START: + dataBuffer.append(SLIP_START) + elif serialByte == SLIP_ESC_END: + dataBuffer.append(SLIP_END) + elif serialByte == SLIP_ESC_ESC: + dataBuffer.append(SLIP_ESC) + else: + dataBuffer.append(SLIP_END) + else: + dataBuffer.append(serialByte) + if not endOfPacket: + raise Exceptions.UARTPacketError( + "Exceeded max timeout of %f seconds." % complete_timeout + ) + return dataBuffer + + # This function read byte chuncks from the serial port and return one byte at a time + # Based on https://github.com/mehdix/pyslip/ + def getSerialByte(self, timeout=None): + serialByte = self.uart.readByte(timeout) + if serialByte is None: + raise Exceptions.SnifferTimeout("Packet read timed out.") + return serialByte + + def handlePacketHistory(self, packet): + # Reads and validates packet counter + if ( + self.lastReceivedPacket is not None + and packet.packetCounter + != (self.lastReceivedPacket.packetCounter + 1) % PACKET_COUNTER_CAP + and self.lastReceivedPacket.packetCounter != 0 + ): + + logging.info( + "gap in packets, between " + + str(self.lastReceivedPacket.packetCounter) + + " and " + + str(packet.packetCounter) + + " packet before: " + + str(self.lastReceivedPacket.packetList) + + " packet after: " + + str(packet.packetList) + ) + + self.lastReceivedPacket = packet + if packet.id in [EVENT_PACKET_DATA_PDU, EVENT_PACKET_ADV_PDU]: + self.lastReceivedTimestampPacket = packet + + def getPacketTime(self, packet): + ble_payload_length = self.lastReceivedPacket.payloadLength - BLE_HEADER_LENGTH + + if packet.phy == PHY_1M: + return 8 * (1 + ble_payload_length) + elif packet.phy == PHY_2M: + return 4 * (2 + ble_payload_length) + elif packet.phy == PHY_CODED: + # blePacket is not assigned if not packet is "OK" (CRC error) + ci = packet.packetList[BLEPACKET_POS + 4] + fec2_block_len = ble_payload_length - 4 - 1 + fec1_block_us = 80 + 256 + 16 + 24 + if ci == PHY_CODED_CI_S8: + return fec1_block_us + 64 * fec2_block_len + 24 + elif ci == PHY_CODED_CI_S2: + return fec1_block_us + 16 * fec2_block_len + 6 + # Unknown PHY or Coding Indicator + return 0 + + def convertPacketListProtoVer2(self, packet): + # Convert to version 2 + packet.packetList[PROTOVER_POS] = 2 + + # Convert to common packet ID + if packet.packetList[ID_POS] == EVENT_PACKET_ADV_PDU: + packet.packetList[ID_POS] = EVENT_PACKET_DATA_PDU + + if packet.packetList[ID_POS] != EVENT_PACKET_DATA_PDU: + # These types do not have a timestamp + return + + # Convert time-stamp to End to Start delta + time_delta = 0 + if ( + self.lastReceivedTimestampPacket is not None + and self.lastReceivedTimestampPacket.valid + ): + time_delta = packet.timestamp - ( + self.lastReceivedTimestampPacket.timestamp + + self.getPacketTime(self.lastReceivedTimestampPacket) + ) + + time_delta = toLittleEndian(time_delta, 4) + packet.packetList[TIMESTAMP_POS] = time_delta[0] + packet.packetList[TIMESTAMP_POS + 1] = time_delta[1] + packet.packetList[TIMESTAMP_POS + 2] = time_delta[2] + packet.packetList[TIMESTAMP_POS + 3] = time_delta[3] + + def handlePacketCompatibility(self, packet): + if ( + self.supportedProtocolVersion == PROTOVER_V2 + and packet.packetList[PROTOVER_POS] > PROTOVER_V2 + ): + self.convertPacketListProtoVer2(packet) + + def setSupportedProtocolVersion(self, supportedProtocolVersion): + if supportedProtocolVersion != PROTOVER_V3: + logging.info( + "Using packet compatibility, converting packets to protocol version %d", + supportedProtocolVersion, + ) + self.supportedProtocolVersion = supportedProtocolVersion + + def getPacket(self, timeout=None): + packetList = [] + try: + packetList = self.decodeFromSLIP(timeout) + except Exceptions.UARTPacketError: # FIXME: This is never thrown... + logging.exception("") + return None + else: + packet = Packet(packetList) + if packet.valid: + self.handlePacketCompatibility(packet) + self.handlePacketHistory(packet) + return packet + + def sendPacket(self, id, payload): + packetList = ( + [HEADER_LENGTH] + + [len(payload)] + + [PROTOVER_V1] + + toLittleEndian(self.packetCounter, 2) + + [id] + + payload + ) + packetList = self.encodeToSLIP(packetList) + self.packetCounter += 1 + self.uart.writeList(packetList) + + def sendScan(self, findScanRsp=False, findAux=False, scanCoded=False): + flags0 = findScanRsp | (findAux << 1) | (scanCoded << 2) + self.sendPacket(REQ_SCAN_CONT, [flags0]) + logging.info("Scan flags: %s" % bin(flags0)) + + def sendFollow( + self, + addr, + followOnlyAdvertisements=False, + followOnlyLegacy=False, + followCoded=False, + ): + flags0 = followOnlyAdvertisements | (followOnlyLegacy << 1) | (followCoded << 2) + logging.info("Follow flags: %s" % bin(flags0)) + self.sendPacket(REQ_FOLLOW, addr + [flags0]) + + def sendPingReq(self): + self.sendPacket(PING_REQ, []) + + def getBytes(self, value, size): + if len(value) < size: + value = [0] * (size - len(value)) + value + else: + value = value[:size] + + return value + + def sendTK(self, TK): + TK = self.getBytes(TK, 16) + self.sendPacket(SET_TEMPORARY_KEY, TK) + logging.info("Sent TK to sniffer: " + str(TK)) + + def sendPrivateKey(self, pk): + pk = self.getBytes(pk, 32) + self.sendPacket(SET_PRIVATE_KEY, pk) + logging.info("Sent private key to sniffer: " + str(pk)) + + def sendLegacyLTK(self, ltk): + ltk = self.getBytes(ltk, 16) + self.sendPacket(SET_LEGACY_LONG_TERM_KEY, ltk) + logging.info("Sent Legacy LTK to sniffer: " + str(ltk)) + + def sendSCLTK(self, ltk): + ltk = self.getBytes(ltk, 16) + self.sendPacket(SET_SC_LONG_TERM_KEY, ltk) + logging.info("Sent SC LTK to sniffer: " + str(ltk)) + + def sendIRK(self, irk): + irk = self.getBytes(irk, 16) + self.sendPacket(SET_IDENTITY_RESOLVING_KEY, irk) + logging.info("Sent IRK to sniffer: " + str(irk)) + + def sendSwitchBaudRate(self, newBaudRate): + self.sendPacket(SWITCH_BAUD_RATE_REQ, toLittleEndian(newBaudRate, 4)) + + def switchBaudRate(self, newBaudRate): + self.uart.switchBaudRate(newBaudRate) + + def sendHopSequence(self, hopSequence): + for chan in hopSequence: + if chan not in VALID_ADV_CHANS: + raise Exceptions.InvalidAdvChannel( + "%s is not an adv channel" % str(chan) + ) + payload = [len(hopSequence)] + hopSequence + [37] * (3 - len(hopSequence)) + self.sendPacket(SET_ADV_CHANNEL_HOP_SEQ, payload) + self.notify("NEW_ADV_HOP_SEQ", {"hopSequence": hopSequence}) + + def sendVersionReq(self): + self.sendPacket(REQ_VERSION, []) + + def sendTimestampReq(self): + self.sendPacket(REQ_TIMESTAMP, []) + + def sendGoIdle(self): + self.sendPacket(GO_IDLE, []) + + +class Packet: + def __init__(self, packetList): + try: + if not packetList: + raise Exceptions.InvalidPacketException( + "packet list not valid: %s" % str(packetList) + ) + + self.protover = packetList[PROTOVER_POS] + + if self.protover > PROTOVER_V3: + logging.exception( + "Unsupported protocol version %s" % str(self.protover) + ) + raise RuntimeError( + "Unsupported protocol version %s" % str(self.protover) + ) + + self.packetCounter = parseLittleEndian( + packetList[PACKETCOUNTER_POS : PACKETCOUNTER_POS + 2] + ) + self.id = packetList[ID_POS] + + if int(self.protover) == PROTOVER_V1: + self.payloadLength = packetList[PAYLOAD_LEN_POS_V1] + else: + self.payloadLength = parseLittleEndian( + packetList[PAYLOAD_LEN_POS : PAYLOAD_LEN_POS + 2] + ) + + self.packetList = packetList + self.readPayload(packetList) + + except Exceptions.InvalidPacketException as e: + logging.error("Invalid packet: %s" % str(e)) + self.OK = False + self.valid = False + except Exception as e: + logging.exception("packet creation error %s" % str(e)) + logging.info("packetList: " + str(packetList)) + self.OK = False + self.valid = False + + def __repr__(self): + return "UART packet, type: " + str(self.id) + ", PC: " + str(self.packetCounter) + + def readPayload(self, packetList): + self.blePacket = None + self.OK = False + + if not self.validatePacketList(packetList): + raise Exceptions.InvalidPacketException( + "packet list not valid: %s" % str(packetList) + ) + else: + self.valid = True + + self.payload = packetList[PAYLOAD_POS : PAYLOAD_POS + self.payloadLength] + + if self.id == EVENT_PACKET_ADV_PDU or self.id == EVENT_PACKET_DATA_PDU: + try: + self.bleHeaderLength = packetList[BLE_HEADER_LEN_POS] + if self.bleHeaderLength == BLE_HEADER_LENGTH: + self.flags = packetList[FLAGS_POS] + self.readFlags() + self.channel = packetList[CHANNEL_POS] + self.rawRSSI = packetList[RSSI_POS] + self.RSSI = -self.rawRSSI + self.eventCounter = parseLittleEndian( + packetList[EVENTCOUNTER_POS : EVENTCOUNTER_POS + 2] + ) + + self.timestamp = parseLittleEndian( + packetList[TIMESTAMP_POS : TIMESTAMP_POS + 4] + ) + + # The hardware adds a padding byte which isn't sent on air. + # We remove it, and update the payload length in the packet list. + if self.phy == PHY_CODED: + self.packetList.pop(BLEPACKET_POS + 6 + 1) + else: + self.packetList.pop(BLEPACKET_POS + 6) + self.payloadLength -= 1 + if self.protover >= PROTOVER_V2: + # Write updated payload length back to the packet list. + payloadLength = toLittleEndian(self.payloadLength, 2) + packetList[PAYLOAD_LEN_POS] = payloadLength[0] + packetList[PAYLOAD_LEN_POS + 1] = payloadLength[1] + else: # PROTOVER_V1 + packetList[PAYLOAD_LEN_POS_V1] = self.payloadLength + else: + logging.info("Invalid BLE Header Length " + str(packetList)) + self.valid = False + + if self.OK: + try: + if self.protover >= PROTOVER_V3: + packet_type = ( + PACKET_TYPE_ADVERTISING + if self.id == EVENT_PACKET_ADV_PDU + else PACKET_TYPE_DATA + ) + else: + packet_type = ( + PACKET_TYPE_ADVERTISING + if packetList[BLEPACKET_POS : BLEPACKET_POS + 4] + == ADV_ACCESS_ADDRESS + else PACKET_TYPE_DATA + ) + + self.blePacket = BlePacket( + packet_type, packetList[BLEPACKET_POS:], self.phy + ) + except Exception as e: + logging.exception("blePacket error %s" % str(e)) + except Exception as e: + # malformed packet + logging.exception("packet error %s" % str(e)) + self.OK = False + elif self.id == PING_RESP: + if self.protover < PROTOVER_V3: + self.version = parseLittleEndian( + packetList[PAYLOAD_POS : PAYLOAD_POS + 2] + ) + elif self.id == RESP_VERSION: + self.version = "".join([chr(i) for i in packetList[PAYLOAD_POS:]]) + elif self.id == RESP_TIMESTAMP: + self.timestamp = parseLittleEndian( + packetList[PAYLOAD_POS : PAYLOAD_POS + 4] + ) + elif self.id == SWITCH_BAUD_RATE_RESP or self.id == SWITCH_BAUD_RATE_REQ: + self.baudRate = parseLittleEndian(packetList[PAYLOAD_POS : PAYLOAD_POS + 4]) + else: + logging.info("Unknown packet ID") + + def readFlags(self): + self.crcOK = not not (self.flags & 1) + self.direction = not not (self.flags & 2) + self.encrypted = not not (self.flags & 4) + self.micOK = not not (self.flags & 8) + self.phy = (self.flags >> 4) & 7 + self.OK = self.crcOK and (self.micOK or not self.encrypted) + + def getList(self): + return self.packetList + + def validatePacketList(self, packetList): + try: + if (self.payloadLength + HEADER_LENGTH) == len(packetList): + return True + else: + return False + except: + logging.exception("Invalid packet: %s" % str(packetList)) + return False + + +class BlePacket: + def __init__(self, type, packetList, phy): + self.type = type + + offset = 0 + offset = self.extractAccessAddress(packetList, offset) + offset = self.extractFormat(packetList, phy, offset) + + if self.type == PACKET_TYPE_ADVERTISING: + offset = self.extractAdvHeader(packetList, offset) + else: + offset = self.extractConnHeader(packetList, offset) + + offset = self.extractLength(packetList, offset) + self.payload = packetList[offset:] + + if self.type == PACKET_TYPE_ADVERTISING: + offset = self.extractAddresses(packetList, offset) + self.extractName(packetList, offset) + + def __repr__(self): + return "BLE packet, AAddr: " + str(self.accessAddress) + + def extractAccessAddress(self, packetList, offset): + self.accessAddress = packetList[offset : offset + 4] + return offset + 4 + + def extractFormat(self, packetList, phy, offset): + self.coded = phy == PHY_CODED + if self.coded: + self.codingIndicator = packetList[offset] & 3 + return offset + 1 + + return offset + + def extractAdvHeader(self, packetList, offset): + self.advType = packetList[offset] & 15 + self.txAddrType = (packetList[offset] >> 6) & 1 + if self.advType in [1, 3, 5]: + self.rxAddrType = (packetList[offset] << 7) & 1 + elif self.advType == 7: + flags = packetList[offset + 2] + if flags & 0x02: + self.rxAddrType = (packetList[offset] << 7) & 1 + return offset + 1 + + def extractConnHeader(self, packetList, offset): + self.llid = packetList[offset] & 3 + self.sn = (packetList[offset] >> 2) & 1 + self.nesn = (packetList[offset] >> 3) & 1 + self.md = (packetList[offset] >> 4) & 1 + return offset + 1 + + def extractAddresses(self, packetList, offset): + addr = None + scanAddr = None + + if self.advType in [0, 1, 2, 4, 6]: + addr = packetList[offset : offset + 6] + addr.reverse() + addr += [self.txAddrType] + offset += 6 + + if self.advType in [3, 5]: + scanAddr = packetList[offset : offset + 6] + scanAddr.reverse() + scanAddr += [self.txAddrType] + offset += 6 + addr = packetList[offset : offset + 6] + addr.reverse() + addr += [self.rxAddrType] + offset += 6 + + if self.advType == 1: + scanAddr = packetList[offset : offset + 6] + scanAddr.reverse() + scanAddr += [self.rxAddrType] + offset += 6 + + if self.advType == 7: + ext_header_len = packetList[offset] & 0x3F + offset += 1 + + ext_header_offset = offset + flags = packetList[offset] + ext_header_offset += 1 + + if flags & 0x01: + addr = packetList[ext_header_offset : ext_header_offset + 6] + addr.reverse() + addr += [self.txAddrType] + ext_header_offset += 6 + + if flags & 0x02: + scanAddr = packetList[ext_header_offset : ext_header_offset + 6] + scanAddr.reverse() + scanAddr += [self.rxAddrType] + ext_header_offset += 6 + + offset += ext_header_len + + self.advAddress = addr + self.scanAddress = scanAddr + return offset + + def extractName(self, packetList, offset): + name = "" + if self.advType in [0, 2, 4, 6, 7]: + i = offset + while i < len(packetList): + length = packetList[i] + if (i + length + 1) > len(packetList) or length == 0: + break + type = packetList[i + 1] + if type == 8 or type == 9: + nameList = packetList[i + 2 : i + length + 1] + name = "" + for j in nameList: + name += chr(j) + i += length + 1 + name = '"' + name + '"' + elif self.advType == 1: + name = "[ADV_DIRECT_IND]" + + self.name = name + + def extractLength(self, packetList, offset): + self.length = packetList[offset] + return offset + 1 + + +def parseLittleEndian(list): + total = 0 + for i in range(len(list)): + total += list[i] << (8 * i) + return total + + +def toLittleEndian(value, size): + list = [0] * size + for i in range(size): + list[i] = (value >> (i * 8)) % 256 + return list diff --git a/modules/wireshark/extcap/SnifferAPI/Pcap.py b/modules/wireshark/extcap/SnifferAPI/Pcap.py new file mode 100644 index 0000000..8b0445a --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Pcap.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import struct + + +# See: +# - https://github.com/pcapng/pcapng +# - https://www.tcpdump.org/linktypes/LINKTYPE_NORDIC_BLE.html +PACKET_HEADER = struct.Struct("= PROTOVER_V3: + if self._last_time is None: + # Timestamp from Host + packet.time = time.time() + else: + # Timestamp using reference and packet timestamp diff + if packet.timestamp < self._last_timestamp: + time_diff = (1 << 32) - (self._last_timestamp - packet.timestamp) + else: + time_diff = packet.timestamp - self._last_timestamp + + packet.time = self._last_time + (time_diff / 1_000_000) + + self._last_time = packet.time + self._last_timestamp = packet.timestamp + else: + # Timestamp from Host + packet.time = time.time() + + self._appendPacket(packet) + + self.notify("NEW_BLE_PACKET", {"packet": packet}) + self._captureHandler.writePacket(packet) + + self._nProcessedPackets += 1 + if packet.OK: + try: + if packet.blePacket.type == PACKET_TYPE_ADVERTISING: + + if self.state == STATE_FOLLOWING and packet.blePacket.advType == 5: + self._connectionAccessAddress = packet.blePacket.accessAddress + + if self.state == STATE_FOLLOWING and packet.blePacket.advType == 4: + newDevice = Devices.Device( + address=packet.blePacket.advAddress, + name=packet.blePacket.name, + RSSI=packet.RSSI, + ) + self._devices.appendOrUpdate(newDevice) + + if self.state == STATE_SCANNING: + if ( + packet.blePacket.advType in [0, 1, 2, 4, 6, 7] + and packet.blePacket.advAddress != None + and packet.crcOK + and not packet.direction + ): + newDevice = Devices.Device( + address=packet.blePacket.advAddress, + name=packet.blePacket.name, + RSSI=packet.RSSI, + ) + self._devices.appendOrUpdate(newDevice) + + except Exception as e: + logging.exception("packet processing error %s" % str(e)) + self.notify("PACKET_PROCESSING_ERROR", {"errorString": str(e)}) + + def _continuouslyPipe(self): + while not self._exit: + try: + packet = self._packetReader.getPacket(timeout=12) + if packet == None or not packet.valid: + raise Exceptions.InvalidPacketException("") + except Exceptions.SnifferTimeout as e: + logging.info(str(e)) + packet = None + except (SerialException, ValueError): + logging.exception("UART read error") + logging.error("Lost contact with sniffer hardware.") + self._doExit() + except Exceptions.InvalidPacketException: + pass + else: + if ( + packet.id == EVENT_PACKET_DATA_PDU + or packet.id == EVENT_PACKET_ADV_PDU + ): + self._processBLEPacket(packet) + elif packet.id == EVENT_FOLLOW: + # This packet has no value for the user. + pass + elif packet.id == EVENT_CONNECT: + self._connectEventPacketCounterValue = packet.packetCounter + self._inConnection = True + # copy it because packets are eventually deleted + self._currentConnectRequest = copy.copy( + self._findPacketByPacketCounter( + self._connectEventPacketCounterValue - 1 + ) + ) + elif packet.id == EVENT_DISCONNECT: + if self._inConnection: + self._packetsInLastConnection = ( + packet.packetCounter - self._connectEventPacketCounterValue + ) + self._inConnection = False + elif packet.id == SWITCH_BAUD_RATE_RESP and self._switchingBaudRate: + self._switchingBaudRate = False + if packet.baudRate == self._proposedBaudRate: + self._packetReader.switchBaudRate(self._proposedBaudRate) + else: + self._switchBaudRate(packet.baudRate) + elif packet.id == PING_RESP: + if hasattr(packet, "version"): + versions = { + 1116: "3.1.0", + 1115: "3.0.0", + 1114: "2.0.0", + 1113: "2.0.0-beta-3", + 1112: "2.0.0-beta-1", + } + self._fwversion = versions.get( + packet.version, "SVN rev: %d" % packet.version + ) + logging.info("Firmware version %s" % self._fwversion) + elif packet.id == RESP_VERSION: + self._fwversion = packet.version + logging.info("Firmware version %s" % self._fwversion) + elif packet.id == RESP_TIMESTAMP: + # Use current time as timestamp reference + self._last_time = time.time() + self._last_timestamp = packet.timestamp + + lt = time.localtime(self._last_time) + usecs = int((self._last_time - int(self._last_time)) * 1_000_000) + logging.info( + f"Firmware timestamp {self._last_timestamp} reference: " + f'{time.strftime("%b %d %Y %X", lt)}.{usecs} {time.strftime("%Z", lt)}' + ) + else: + logging.info("Unknown packet ID") + + def _findPacketByPacketCounter(self, packetCounterValue): + with self._packetListLock: + for i in range(-1, -1 - len(self._packets), -1): + # iterate backwards through packets + if self._packets[i].packetCounter == packetCounterValue: + return self._packets[i] + return None + + def _startScanning(self, findScanRsp=False, findAux=False, scanCoded=False): + logging.info("starting scan") + + if self.state == STATE_FOLLOWING: + logging.info("Stopped sniffing device") + + self._setState(STATE_SCANNING) + self._packetReader.sendScan(findScanRsp, findAux, scanCoded) + self._packetReader.sendTK([0]) + + def _doExit(self): + self._exit = True + self.notify("APP_EXIT") + self._packetReader.doExit() + # Clear method references to avoid uncollectable cyclic references + self.clearCallbacks() + self._devices.clearCallbacks() + + def _startFollowing( + self, + device, + followOnlyAdvertisements=False, + followOnlyLegacy=False, + followCoded=False, + ): + self._devices.setFollowed(device) + logging.info( + "Sniffing device " + + str(self._devices.index(device)) + + ' - "' + + device.name + + '"' + ) + self._packetReader.sendFollow( + device.address, followOnlyAdvertisements, followOnlyLegacy, followCoded + ) + self._setState(STATE_FOLLOWING) + + def _clearDevices(self): + self._devices.clear() + + def _appendPacket(self, packet): + with self._packetListLock: + if len(self._packets) > 100000: + self._packets = self._packets[20000:] + self._packets.append(packet) + + def _getPackets(self, number=-1): + with self._packetListLock: + returnList = self._packets[0:number] + self._packets = self._packets[number:] + return returnList + + def _clearPackets(self): + with self._packetListLock: + del self._packets[:] diff --git a/modules/wireshark/extcap/SnifferAPI/Types.py b/modules/wireshark/extcap/SnifferAPI/Types.py new file mode 100644 index 0000000..eac7609 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Types.py @@ -0,0 +1,90 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SLIP_START = 0xAB +SLIP_END = 0xBC +SLIP_ESC = 0xCD +SLIP_ESC_START = SLIP_START + 1 +SLIP_ESC_END = SLIP_END + 1 +SLIP_ESC_ESC = SLIP_ESC + 1 + +PROTOVER_V3 = 3 +PROTOVER_V2 = 2 +PROTOVER_V1 = 1 + +# UART protocol packet codes (see sniffer_uart_protocol.pdf) +REQ_FOLLOW = 0x00 +EVENT_FOLLOW = 0x01 +EVENT_PACKET_ADV_PDU = 0x02 +EVENT_CONNECT = 0x05 +EVENT_PACKET_DATA_PDU = 0x06 +REQ_SCAN_CONT = 0x07 +EVENT_DISCONNECT = 0x09 +SET_TEMPORARY_KEY = 0x0C +PING_REQ = 0x0D +PING_RESP = 0x0E +SWITCH_BAUD_RATE_REQ = 0x13 +SWITCH_BAUD_RATE_RESP = 0x14 +SET_ADV_CHANNEL_HOP_SEQ = 0x17 +SET_PRIVATE_KEY = 0x18 +SET_LEGACY_LONG_TERM_KEY = 0x19 +SET_SC_LONG_TERM_KEY = 0x1A +REQ_VERSION = 0x1B +RESP_VERSION = 0x1C +REQ_TIMESTAMP = 0x1D +RESP_TIMESTAMP = 0x1E +SET_IDENTITY_RESOLVING_KEY = 0x1F +GO_IDLE = 0xFE + +PACKET_TYPE_UNKNOWN = 0x00 +PACKET_TYPE_ADVERTISING = 0x01 +PACKET_TYPE_DATA = 0x02 + +ADV_TYPE_ADV_IND = 0x0 +ADV_TYPE_ADV_DIRECT_IND = 0x1 +ADV_TYPE_ADV_NONCONN_IND = 0x2 +ADV_TYPE_ADV_SCAN_IND = 0x6 +ADV_TYPE_SCAN_REQ = 0x3 +ADV_TYPE_SCAN_RSP = 0x4 +ADV_TYPE_CONNECT_REQ = 0x5 +ADV_TYPE_ADV_EXT_IND = 0x7 + +PHY_1M = 0 +PHY_2M = 1 +PHY_CODED = 2 + +PHY_CODED_CI_S8 = 0 +PHY_CODED_CI_S2 = 1 diff --git a/modules/wireshark/extcap/SnifferAPI/UART.py b/modules/wireshark/extcap/SnifferAPI/UART.py new file mode 100644 index 0000000..ecd16d2 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/UART.py @@ -0,0 +1,238 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import collections +import logging +import serial +from threading import Thread, Event + +import serial.tools.list_ports as list_ports + +from . import Exceptions +from . import Packet +from . import Filelock + +import os + +if os.name == "posix": + import termios + +SNIFFER_OLD_DEFAULT_BAUDRATE = 460800 +# Baudrates that should be tried (add more if required) +SNIFFER_BAUDRATES = [1000000, 460800] + + +def find_sniffer(write_data=False): + open_ports = list_ports.comports() + + sniffers = [] + for port in [x.device for x in open_ports]: + for rate in SNIFFER_BAUDRATES: + reader = None + l_errors = [ + serial.SerialException, + ValueError, + Exceptions.LockedException, + OSError, + ] + if os.name == "posix": + l_errors.append(termios.error) + try: + reader = Packet.PacketReader(portnum=port, baudrate=rate) + try: + if write_data: + reader.sendPingReq() + _ = reader.decodeFromSLIP(0.1, complete_timeout=0.1) + else: + _ = reader.decodeFromSLIP(0.3, complete_timeout=0.3) + + # FIXME: Should add the baud rate here, but that will be a breaking change + sniffers.append(port) + break + except (Exceptions.SnifferTimeout, Exceptions.UARTPacketError): + pass + except tuple(l_errors): + continue + finally: + if reader is not None: + reader.doExit() + return sniffers + + +def find_sniffer_baudrates(port, write_data=False): + for rate in SNIFFER_BAUDRATES: + reader = None + try: + reader = Packet.PacketReader(portnum=port, baudrate=rate) + try: + if write_data: + reader.sendPingReq() + _ = reader.decodeFromSLIP(0.1, complete_timeout=0.1) + else: + _ = reader.decodeFromSLIP(0.3, complete_timeout=0.3) + + # TODO: possibly include additional rates based on protocol version + return {"default": rate, "other": []} + except (Exceptions.SnifferTimeout, Exceptions.UARTPacketError): + pass + finally: + if reader is not None: + reader.doExit() + return None + + +class Uart: + def __init__(self, portnum=None, baudrate=None): + self.ser = None + try: + if baudrate is not None and baudrate not in SNIFFER_BAUDRATES: + raise Exception("Invalid baudrate: " + str(baudrate)) + + logging.info("Opening serial port {}".format(portnum)) + + self.portnum = portnum + if self.portnum: + Filelock.lock(portnum) + + self.ser = serial.Serial( + port=portnum, baudrate=9600, rtscts=True, exclusive=True + ) + self.ser.baudrate = baudrate + + except Exception: + if self.ser: + self.ser.close() + self.ser = None + raise + + self.read_queue = collections.deque() + self.read_queue_has_data = Event() + + self.worker_thread = Thread(target=self._read_worker) + self.reading = True + self.worker_thread.setDaemon(True) + self.worker_thread.start() + + def _read_worker(self): + self.ser.reset_input_buffer() + while self.reading: + try: + # Read any data available, or wait for at least one byte + data_read = self.ser.read(self.ser.in_waiting or 1) + # logging.info('type: {}'.format(data_read.__class__)) + self._read_queue_extend(data_read) + except serial.SerialException as e: + logging.info("Unable to read UART: %s" % e) + self.reading = False + return + + def close(self): + if self.ser: + logging.info("closing UART") + self.reading = False + # Wake any threads waiting on the queue + self.read_queue_has_data.set() + if hasattr(self.ser, "cancel_read"): + self.ser.cancel_read() + self.worker_thread.join() + self.ser.close() + else: + self.ser.close() + self.worker_thread.join() + self.ser = None + + if self.portnum: + Filelock.unlock(self.portnum) + + def __del__(self): + self.close() + + def switchBaudRate(self, newBaudRate): + self.ser.baudrate = newBaudRate + + def readByte(self, timeout=None): + r = self._read_queue_get(timeout) + return r + + def writeList(self, array): + try: + self.ser.write(array) + except serial.SerialTimeoutException: + logging.info("Got write timeout, ignoring error") + + except serial.SerialException as e: + self.ser.close() + raise e + + def _read_queue_extend(self, data): + if len(data) > 0: + self.read_queue.extend(data) + self.read_queue_has_data.set() + + def _read_queue_get(self, timeout=None): + data = None + if self.read_queue_has_data.wait(timeout): + self.read_queue_has_data.clear() + try: + data = self.read_queue.popleft() + except IndexError: + # This will happen when the class is destroyed + return None + if len(self.read_queue) > 0: + self.read_queue_has_data.set() + return data + + +def list_serial_ports(): + # Scan for available ports. + return list_ports.comports() + + +if __name__ == "__main__": + import time + + t_start = time.time() + s = find_sniffer() + tn = time.time() + print(s) + print("find_sniffer took %f seconds" % (tn - t_start)) + for p in s: + t = time.time() + print(find_sniffer_baudrates(p)) + tn = time.time() + print("find_sniffer_baudrate took %f seconds" % (tn - t)) + tn = time.time() + print("total runtime %f" % (tn - t_start)) diff --git a/modules/wireshark/extcap/SnifferAPI/__init__.py b/modules/wireshark/extcap/SnifferAPI/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/wireshark/extcap/SnifferAPI/version.py b/modules/wireshark/extcap/SnifferAPI/version.py new file mode 100644 index 0000000..5b94c32 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/version.py @@ -0,0 +1,37 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +VERSION_STRING = "4.1.1" diff --git a/modules/wireshark/extcap/nrf_sniffer_ble.py b/modules/wireshark/extcap/nrf_sniffer_ble.py new file mode 100644 index 0000000..1aa1380 --- /dev/null +++ b/modules/wireshark/extcap/nrf_sniffer_ble.py @@ -0,0 +1,991 @@ +#!/usr/bin/env python3 + +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +""" +Wireshark extcap wrapper for the nRF Sniffer for Bluetooth LE by Nordic Semiconductor. +""" + +import os +import sys +import argparse +import re +import time +import struct +import logging + +from SnifferAPI import Logger + +try: + import serial +except ImportError: + Logger.initLogger() + logging.error( + f'pyserial not found, please run: "{sys.executable} -m pip install -r requirements.txt" and retry' + ) + sys.exit( + f'pyserial not found, please run: "{sys.executable} -m pip install -r requirements.txt" and retry' + ) + +from SnifferAPI import Sniffer, UART, Devices, Pcap, Exceptions + +ERROR_USAGE = 0 +ERROR_ARG = 1 +ERROR_INTERFACE = 2 +ERROR_FIFO = 3 +ERROR_INTERNAL = 4 + +CTRL_CMD_INIT = 0 +CTRL_CMD_SET = 1 +CTRL_CMD_ADD = 2 +CTRL_CMD_REMOVE = 3 +CTRL_CMD_ENABLE = 4 +CTRL_CMD_DISABLE = 5 +CTRL_CMD_STATUSBAR = 6 +CTRL_CMD_INFO_MSG = 7 +CTRL_CMD_WARN_MSG = 8 +CTRL_CMD_ERROR_MSG = 9 + +CTRL_ARG_DEVICE = 0 +CTRL_ARG_KEY_TYPE = 1 +CTRL_ARG_KEY_VAL = 2 +CTRL_ARG_ADVHOP = 3 +CTRL_ARG_HELP = 4 +CTRL_ARG_RESTORE = 5 +CTRL_ARG_LOG = 6 +CTRL_ARG_DEVICE_CLEAR = 7 +CTRL_ARG_NONE = 255 + +CTRL_KEY_TYPE_PASSKEY = 0 +CTRL_KEY_TYPE_OOB = 1 +CTRL_KEY_TYPE_LEGACY_LTK = 2 +CTRL_KEY_TYPE_SC_LTK = 3 +CTRL_KEY_TYPE_DH_PRIVATE_KEY = 4 +CTRL_KEY_TYPE_IRK = 5 +CTRL_KEY_TYPE_ADD_ADDR = 6 +CTRL_KEY_TYPE_FOLLOW_ADDR = 7 + +fn_capture = None +fn_ctrl_in = None +fn_ctrl_out = None + +extcap_log_handler = None +extcap_version = None + +# Wireshark nRF Sniffer for Bluetooth LE Toolbar will always cache the last used key and adv hop and send +# this when starting a capture. To ensure that the key and adv hop is always shown correctly +# in the Toolbar, even if the user has changed it but not applied it, we send the last used +# key and adv hop back as a default value. +last_used_key_type = CTRL_KEY_TYPE_PASSKEY +last_used_key_val = "" +last_used_advhop = "37,38,39" + +zero_addr = "[00,00,00,00,00,00,0]" + +# While searching for a selected Device we must not write packets to the pipe until +# the device is found to avoid getting advertising packets from other devices. +write_new_packets = False + +# The RSSI capture filter value given from Wireshark. +rssi_filter = 0 + +# The RSSI filtering is not on when in follow mode. +in_follow_mode = False + +# nRF Sniffer for Bluetooth LE interface option to only capture advertising packets +capture_only_advertising = False +capture_only_legacy_advertising = False +capture_scan_response = True +capture_scan_aux_pointer = True +capture_coded = False + + +def extcap_config(interface): + """List configuration for the given interface""" + print( + "arg {number=0}{call=--only-advertising}{display=Only advertising packets}" + "{tooltip=The sniffer will only capture advertising packets from the selected device}{type=boolflag}{save=true}" + ) + print( + "arg {number=1}{call=--only-legacy-advertising}{display=Only legacy advertising packets}" + "{tooltip=The sniffer will only capture legacy advertising packets from the selected device}{type=boolflag}{save=true}" + ) + print( + "arg {number=2}{call=--scan-follow-rsp}{display=Find scan response data}" + "{tooltip=The sniffer will follow scan requests and scan responses in scan mode}{type=boolflag}{default=true}{save=true}" + ) + print( + "arg {number=3}{call=--scan-follow-aux}{display=Find auxiliary pointer data}" + "{tooltip=The sniffer will follow aux pointers in scan mode}{type=boolflag}{default=true}{save=true}" + ) + print( + "arg {number=3}{call=--coded}{display=Scan and follow devices on LE Coded PHY}" + "{tooltip=Scan for devices and follow advertiser on LE Coded PHY}{type=boolflag}{default=false}{save=true}" + ) + + +def extcap_dlts(interface): + """List DLTs for the given interface""" + print("dlt {number=272}{name=NORDIC_BLE}{display=nRF Sniffer for Bluetooth LE}") + + +def get_baud_rates(interface): + if not hasattr(serial, "__version__") or not serial.__version__.startswith("3."): + raise RuntimeError( + "Too old version of python 'serial' Library. Version 3 required." + ) + return UART.find_sniffer_baudrates(interface) + + +def get_interfaces(): + if not hasattr(serial, "__version__") or not serial.__version__.startswith("3."): + raise RuntimeError( + "Too old version of python 'serial' Library. Version 3 required." + ) + + devices = UART.find_sniffer() + return devices + + +def extcap_interfaces(): + """List available interfaces to capture from""" + print( + "extcap {version=%s}{display=nRF Sniffer for Bluetooth LE}" + "{help=https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Sniffer-for-Bluetooth-LE}" + % Sniffer.VERSION_STRING + ) + + for interface_port in get_interfaces(): + if sys.platform == "win32": + print( + "interface {value=%s-%s}{display=nRF Sniffer for Bluetooth LE %s}" + % (interface_port, extcap_version, interface_port) + ) + else: + print( + "interface {value=%s-%s}{display=nRF Sniffer for Bluetooth LE}" + % (interface_port, extcap_version) + ) + + print( + "control {number=%d}{type=selector}{display=Device}{tooltip=Device list}" + % CTRL_ARG_DEVICE + ) + print( + "control {number=%d}{type=selector}{display=Key}{tooltip=}" % CTRL_ARG_KEY_TYPE + ) + print( + "control {number=%d}{type=string}{display=Value}" + "{tooltip=6 digit passkey or 16 or 32 bytes encryption key in hexadecimal starting with '0x', big endian format." + "If the entered key is shorter than 16 or 32 bytes, it will be zero-padded in front'}" + "{validation=\\b^(([0-9]{6})|(0x[0-9a-fA-F]{1,64})|([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random))$\\b}" + % CTRL_ARG_KEY_VAL + ) + print( + "control {number=%d}{type=string}{display=Adv Hop}" + "{default=37,38,39}" + "{tooltip=Advertising channel hop sequence. " + "Change the order in which the sniffer switches advertising channels. " + "Valid channels are 37, 38 and 39 separated by comma.}" + r"{validation=^\s*((37|38|39)\s*,\s*){0,2}(37|38|39){1}\s*$}{required=true}" + % CTRL_ARG_ADVHOP + ) + print( + "control {number=%d}{type=button}{display=Clear}{tooltop=Clear or remove device from Device list}" + % CTRL_ARG_DEVICE_CLEAR + ) + print( + "control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Access user guide (launches browser)}" + % CTRL_ARG_HELP + ) + print( + "control {number=%d}{type=button}{role=restore}{display=Defaults}{tooltip=Resets the user interface and clears the log file}" + % CTRL_ARG_RESTORE + ) + print( + "control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Log per interface}" + % CTRL_ARG_LOG + ) + + print( + "value {control=%d}{value= }{display=All advertising devices}{default=true}" + % CTRL_ARG_DEVICE + ) + print( + "value {control=%d}{value=%s}{display=Follow IRK}" + % (CTRL_ARG_DEVICE, zero_addr) + ) + + print( + "value {control=%d}{value=%d}{display=Legacy Passkey}{default=true}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_PASSKEY) + ) + print( + "value {control=%d}{value=%d}{display=Legacy OOB data}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_OOB) + ) + print( + "value {control=%d}{value=%d}{display=Legacy LTK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_LEGACY_LTK) + ) + print( + "value {control=%d}{value=%d}{display=SC LTK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_SC_LTK) + ) + print( + "value {control=%d}{value=%d}{display=SC Private Key}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_DH_PRIVATE_KEY) + ) + print( + "value {control=%d}{value=%d}{display=IRK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_IRK) + ) + print( + "value {control=%d}{value=%d}{display=Add LE address}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_ADD_ADDR) + ) + print( + "value {control=%d}{value=%d}{display=Follow LE address}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_FOLLOW_ADDR) + ) + + +def string_address(address): + """Make a string representation of the address""" + if len(address) < 7: + return None + + addr_string = "" + + for i in range(5): + addr_string += format(address[i], "02x") + ":" + addr_string += format(address[5], "02x") + " " + + if address[6]: + addr_string += " random " + else: + addr_string += " public " + + return addr_string + + +def control_read(): + """Read a message from the control channel""" + header = fn_ctrl_in.read(6) + if not header: + # Ref. https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects: + # > If the end of the file has been reached, f.read() will return an + # > empty string ('') + return None, None, None + + _, _, length, arg, typ = struct.unpack(">sBHBB", header) + + payload = bytearray() + if length > 2: + payload = fn_ctrl_in.read(length - 2) + + return arg, typ, payload + + +def control_write(arg, typ, message): + """Write the message to the control channel""" + + if not fn_ctrl_out: + # No control out has been opened + return + + packet = bytearray() + packet += struct.pack(">BBHBB", ord("T"), 0, len(message) + 2, arg, typ) + packet += message.encode("utf-8") + + fn_ctrl_out.write(packet) + + +def capture_write(message): + """Write the message to the capture pipe""" + fn_capture.write(message) + fn_capture.flush() + + +def new_packet(notification): + """A new Bluetooth LE packet has arrived""" + if write_new_packets == True: + packet = notification.msg["packet"] + + if rssi_filter == 0 or in_follow_mode == True or packet.RSSI > rssi_filter: + p = bytes([packet.boardId] + packet.getList()) + capture_write(Pcap.create_packet(p, packet.time)) + + +def device_added(notification): + """A device is added or updated""" + device = notification.msg + + # Only add devices matching RSSI filter + if rssi_filter == 0 or device.RSSI > rssi_filter: + # Extcap selector uses \0 character to separate value and display value, + # therefore the display value cannot contain the \0 character as this + # would lead to truncation of the display value. + display = ( + device.name.replace("\0", "\\0") + + (" " + str(device.RSSI) + " dBm " if device.RSSI != 0 else " ") + + string_address(device.address) + ) + + message = str(device.address) + "\0" + display + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, message) + + +def device_removed(notification): + """A device is removed""" + device = notification.msg + display = device.name + " " + string_address(device.address) + + message = "" + message += str(device.address) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_REMOVE, message) + logging.info("Removed: " + display) + + +def devices_cleared(notification): + """Devices have been cleared""" + message = "" + control_write(CTRL_ARG_DEVICE, CTRL_CMD_REMOVE, message) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, " " + "\0" + "All advertising devices") + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, zero_addr + "\0" + "Follow IRK") + control_write(CTRL_ARG_DEVICE, CTRL_CMD_SET, " ") + + +def handle_control_command(sniffer, arg, typ, payload): + """Handle command from control channel""" + global last_used_key_type + + if arg == CTRL_ARG_DEVICE: + if payload == b" ": + scan_for_devices(sniffer) + else: + values = payload + values = values.replace(b"[", b"") + values = values.replace(b"]", b"") + device_address = values.split(b",") + + logging.info("follow_device: {}".format(device_address)) + for i in range(6): + device_address[i] = int(device_address[i]) + + device_address[6] = 1 if device_address[6] == b" 1" else 0 + + device = Devices.Device(address=device_address, name='""', RSSI=0) + + follow_device(sniffer, device) + + elif arg == CTRL_ARG_DEVICE_CLEAR: + clear_devices(sniffer) + elif arg == CTRL_ARG_KEY_TYPE: + last_used_key_type = int(payload.decode("utf-8")) + elif arg == CTRL_ARG_KEY_VAL: + set_key_value(sniffer, payload) + elif arg == CTRL_ARG_ADVHOP: + set_advhop(sniffer, payload) + + +def control_read_initial_values(sniffer): + """Read initial control values""" + initialized = False + + while not initialized: + arg, typ, payload = control_read() + if typ == CTRL_CMD_INIT: + initialized = True + else: + handle_control_command(sniffer, arg, typ, payload) + + +def control_write_defaults(): + """Write default control values""" + control_write(CTRL_ARG_KEY_TYPE, CTRL_CMD_SET, str(last_used_key_type)) + control_write(CTRL_ARG_KEY_VAL, CTRL_CMD_SET, last_used_key_val) + control_write(CTRL_ARG_ADVHOP, CTRL_CMD_SET, last_used_advhop) + + +def scan_for_devices(sniffer): + """Start scanning for advertising devices""" + global in_follow_mode + if sniffer.state == 2: + log = "Scanning all advertising devices" + logging.info(log) + sniffer.scan(capture_scan_response, capture_scan_aux_pointer, capture_coded) + + in_follow_mode = False + + +def clear_devices(sniffer): + """Clear the advertising devices list""" + global in_follow_mode + + sniffer.clearDevices() + scan_for_devices(sniffer) + + in_follow_mode = False + + +def follow_device(sniffer, device): + """Follow the selected device""" + global write_new_packets, in_follow_mode + + sniffer.follow( + device, capture_only_advertising, capture_only_legacy_advertising, capture_coded + ) + time.sleep(0.1) + + in_follow_mode = True + logging.info("Following " + string_address(device.address)) + + +def set_key_value(sniffer, payload): + """Send key value to device""" + global last_used_key_val + + payload = payload.decode("utf-8") + last_used_key_val = payload + + if last_used_key_type == CTRL_KEY_TYPE_PASSKEY: + if re.match("^[0-9]{6}$", payload): + set_passkey(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_OOB: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_OOB(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_DH_PRIVATE_KEY: + if re.match("^0[xX][0-9A-Za-z]{1,64}$", payload): + set_dh_private_key(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_LEGACY_LTK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_legacy_ltk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_SC_LTK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_sc_ltk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_IRK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_irk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_ADD_ADDR: + if re.match( + "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random)$", payload + ): + add_address(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_FOLLOW_ADDR: + if re.match( + "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random)$", payload + ): + follow_address(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + else: + logging.info("Invalid key type: " + str(last_used_key_type)) + + +def parse_hex(value): + if len(value) % 2 != 0: + value = "0" + value + + a = list(value) + return [int(x + y, 16) for x, y in zip(a[::2], a[1::2])] + + +def set_passkey(sniffer, payload): + """Send passkey to device""" + passkey = [] + logging.info("Setting Passkey: " + payload) + init_payload = int(payload, 10) + if len(payload) >= 6: + passkey = [] + passkey += [(init_payload >> 16) & 0xFF] + passkey += [(init_payload >> 8) & 0xFF] + passkey += [(init_payload >> 0) & 0xFF] + + sniffer.sendTK(passkey) + + +def set_OOB(sniffer, payload): + """Send OOB to device""" + logging.info("Setting OOB data: " + payload) + sniffer.sendTK(parse_hex(payload)) + + +def set_dh_private_key(sniffer, payload): + """Send Diffie-Hellman private key to device""" + logging.info("Setting DH private key: " + payload) + sniffer.sendPrivateKey(parse_hex(payload)) + + +def set_legacy_ltk(sniffer, payload): + """Send Legacy Long Term Key (LTK) to device""" + logging.info("Setting Legacy LTK: " + payload) + sniffer.sendLegacyLTK(parse_hex(payload)) + + +def set_sc_ltk(sniffer, payload): + """Send LE secure connections Long Term Key (LTK) to device""" + logging.info("Setting SC LTK: " + payload) + sniffer.sendSCLTK(parse_hex(payload)) + + +def set_irk(sniffer, payload): + """Send Identity Resolving Key (IRK) to device""" + logging.info("Setting IRK: " + payload) + sniffer.sendIRK(parse_hex(payload)) + + +def add_address(sniffer, payload): + """Add LE address to device list""" + logging.info("Adding LE address: " + payload) + + (addr, addr_type) = payload.split(" ") + device = [int(a, 16) for a in addr.split(":")] + + device.append(1 if addr_type == "random" else 0) + + new_device = Devices.Device(address=device, name='""', RSSI=0) + sniffer.addDevice(new_device) + + +def follow_address(sniffer, payload): + """Add LE address to device list""" + logging.info("Adding LE address: " + payload) + + (addr, addr_type) = payload.split(" ") + device = [int(a, 16) for a in addr.split(":")] + + device.append(1 if addr_type == "random" else 0) + + new_device = Devices.Device(address=device, name='""', RSSI=0) + sniffer.addDevice(new_device) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_SET, f"{new_device.address}") + follow_device(sniffer, new_device) + + +def set_advhop(sniffer, payload): + """Set advertising channel hop sequence""" + global last_used_advhop + + payload = payload.decode("utf-8") + + last_used_advhop = payload + + hops = [int(channel) for channel in payload.split(",")] + + sniffer.setAdvHopSequence(hops) + + log = "AdvHopSequence: " + str(hops) + logging.info(log) + + +def control_loop(sniffer): + """Main loop reading control messages""" + arg_read = CTRL_ARG_NONE + while arg_read is not None: + arg_read, typ, payload = control_read() + handle_control_command(sniffer, arg_read, typ, payload) + + +def error_interface_not_found(interface, fifo): + log = "nRF Sniffer for Bluetooth LE could not find interface: " + interface + control_write(CTRL_ARG_NONE, CTRL_CMD_ERROR_MSG, log) + extcap_close_fifo(fifo) + sys.exit(ERROR_INTERFACE) + + +def validate_interface(interface, fifo): + """Check if interface exists""" + if sys.platform != "win32" and not os.path.exists(interface): + error_interface_not_found(interface, fifo) + + +def get_default_baudrate(interface, fifo): + """Return the baud rate that interface is running at, or exit if the board is not found""" + rates = get_baud_rates(interface) + if rates is None: + error_interface_not_found(interface, fifo) + return rates["default"] + + +def get_supported_protocol_version(extcap_version): + """Return the maximum supported Packet Protocol Version""" + if extcap_version == "None": + return 2 + + (major, minor) = extcap_version.split(".") + + major = int(major) + minor = int(minor) + + if major > 3 or (major == 3 and minor >= 4): + return 3 + else: + return 2 + + +def setup_extcap_log_handler(): + """Add the a handler that emits log messages through the extcap control out channel""" + global extcap_log_handler + extcap_log_handler = ExtcapLoggerHandler() + Logger.addLogHandler(extcap_log_handler) + control_write(CTRL_ARG_LOG, CTRL_CMD_SET, "") + + +def teardown_extcap_log_handler(): + """Remove and reset the extcap log handler""" + global extcap_log_handler + if extcap_log_handler: + Logger.removeLogHandler(extcap_log_handler) + extcap_log_handler = None + + +def sniffer_capture(interface, baudrate, fifo, control_in, control_out): + """Start the sniffer to capture packets""" + global fn_capture, fn_ctrl_in, fn_ctrl_out, write_new_packets, extcap_log_handler + + try: + fn_capture = open(fifo, "wb", 0) + + if control_out is not None: + fn_ctrl_out = open(control_out, "wb", 0) + setup_extcap_log_handler() + + if control_in is not None: + fn_ctrl_in = open(control_in, "rb", 0) + + logging.info("Log started at %s", time.strftime("%c")) + + interface, extcap_version = interface.split("-") + logging.info("Extcap version %s", str(extcap_version)) + + capture_write(Pcap.get_global_header()) + validate_interface(interface, fifo) + if baudrate is None: + baudrate = get_default_baudrate(interface, fifo) + + sniffer = Sniffer.Sniffer(interface, baudrate) + sniffer.subscribe("NEW_BLE_PACKET", new_packet) + sniffer.subscribe("DEVICE_ADDED", device_added) + sniffer.subscribe("DEVICE_UPDATED", device_added) + sniffer.subscribe("DEVICE_REMOVED", device_removed) + sniffer.subscribe("DEVICES_CLEARED", devices_cleared) + sniffer.setAdvHopSequence([37, 38, 39]) + sniffer.setSupportedProtocolVersion( + get_supported_protocol_version(extcap_version) + ) + logging.info("Sniffer created") + + logging.info("Software version: %s" % sniffer.swversion) + sniffer.getFirmwareVersion() + sniffer.getTimestamp() + sniffer.start() + logging.info("sniffer started") + sniffer.scan(capture_scan_response, capture_scan_aux_pointer, capture_coded) + logging.info("scanning started") + + if fn_ctrl_in is not None and fn_ctrl_out is not None: + # First read initial control values + control_read_initial_values(sniffer) + + # Then write default values + control_write_defaults() + logging.info("defaults written") + + # Start receiving packets + write_new_packets = True + + # Start the control loop + logging.info("control loop") + control_loop(sniffer) + logging.info("exiting control loop") + + else: + logging.info("") + # Start receiving packets + write_new_packets = True + while True: + # Wait for keyboardinterrupt + pass + + except Exceptions.LockedException as e: + logging.info("{}".format(e.message)) + + except OSError: + # We'll get OSError=22 when/if wireshark kills the pipe(s) on capture + # stop. + pass + + finally: + # The first thing we should do is to tear down the extcap log handler. + # This might already have triggered an OSError, or we will trigger one + # by attempting to log at this point. + teardown_extcap_log_handler() + + # Safe to use logging again. + logging.info("Tearing down") + + sniffer.doExit() + if fn_capture is not None and not fn_capture.closed: + fn_capture.close() + + if fn_ctrl_in is not None and not fn_ctrl_in.closed: + fn_ctrl_in.close() + + if fn_ctrl_out is not None and not fn_ctrl_out.closed: + fn_ctrl_out.close() + + fn_capture = None + fn_ctrl_out = None + fn_ctrl_in = None + + logging.info("Exiting") + + +def extcap_close_fifo(fifo): + """ "Close extcap fifo""" + if not os.path.exists(fifo): + print("FIFO does not exist!", file=sys.stderr) + return + + # This is apparently needed to workaround an issue on Windows/macOS + # where the message cannot be read. (really?) + fh = open(fifo, "wb", 0) + fh.close() + + +class ExtcapLoggerHandler(logging.Handler): + """Handler used to display all logging messages in extcap""" + + def emit(self, record): + """Send log message to extcap""" + message = record.message.replace("\0", "\\0") + log_message = f"{record.levelname}: {message}\n" + control_write(CTRL_ARG_LOG, CTRL_CMD_ADD, log_message) + + +def parse_capture_filter(capture_filter): + """ "Parse given capture filter""" + global rssi_filter + m = re.search(r"^\s*rssi\s*(>=?)\s*(-?[0-9]+)\s*$", capture_filter, re.IGNORECASE) + if m: + rssi_filter = int(m.group(2)) + if rssi_filter > -10 or rssi_filter < -256: + print("Illegal RSSI value, must be between -10 and -256") + # Handle >= by modifying the threshold, since comparisons are always done with + # the > operator + if m.group(1) == ">=": + rssi_filter = rssi_filter - 1 + else: + print('Filter syntax: "RSSI >= -value"') + + +import atexit + + +@atexit.register +def goodbye(): + logging.info("Exiting PID {}".format(os.getpid())) + + +if __name__ == "__main__": + + # Capture options + parser = argparse.ArgumentParser( + description="Nordic Semiconductor nRF Sniffer for Bluetooth LE extcap plugin" + ) + + # Extcap Arguments + parser.add_argument("--capture", help="Start the capture", action="store_true") + + parser.add_argument( + "--extcap-interfaces", + help="List available interfaces to capture from", + action="store_true", + ) + + parser.add_argument("--extcap-interface", help="The interface to capture from") + + parser.add_argument( + "--extcap-dlts", help="List DLTs for the given interface", action="store_true" + ) + + parser.add_argument( + "--extcap-config", + help="List configurations for the given interface", + action="store_true", + ) + + parser.add_argument( + "--extcap-capture-filter", + help="Used together with capture to provide a capture filter", + ) + + parser.add_argument( + "--fifo", help="Use together with capture to provide the fifo to dump data to" + ) + + parser.add_argument( + "--extcap-control-in", + help="Used together with capture to get control messages from toolbar", + ) + + parser.add_argument( + "--extcap-control-out", + help="Used together with capture to send control messages to toolbar", + ) + + parser.add_argument("--extcap-version", help="Set extcap supported version") + + # Interface Arguments + parser.add_argument("--device", help="Device", default="") + parser.add_argument("--baudrate", type=int, help="The sniffer baud rate") + parser.add_argument( + "--only-advertising", help="Only advertising packets", action="store_true" + ) + parser.add_argument( + "--only-legacy-advertising", + help="Only legacy advertising packets", + action="store_true", + ) + parser.add_argument( + "--scan-follow-rsp", help="Find scan response data ", action="store_true" + ) + parser.add_argument( + "--scan-follow-aux", help="Find auxiliary pointer data", action="store_true" + ) + parser.add_argument( + "--coded", help="Scan and follow on LE Coded PHY", action="store_true" + ) + + logging.info("Started PID {}".format(os.getpid())) + + try: + args, unknown = parser.parse_known_args() + logging.info(args) + + except argparse.ArgumentError as exc: + print("%s" % exc, file=sys.stderr) + fifo_found = False + fifo = "" + for arg in sys.argv: + if arg == "--fifo" or arg == "--extcap-fifo": + fifo_found = True + elif fifo_found: + fifo = arg + break + extcap_close_fifo(fifo) + sys.exit(ERROR_ARG) + + if len(sys.argv) <= 1: + parser.exit("No arguments given!") + + if args.extcap_version: + extcap_version = args.extcap_version + + if args.extcap_capture_filter: + parse_capture_filter(args.extcap_capture_filter) + if args.extcap_interface and len(sys.argv) == 5: + sys.exit(0) + + if not args.extcap_interfaces and args.extcap_interface is None: + parser.exit("An interface must be provided or the selection must be displayed") + + if args.extcap_interfaces or args.extcap_interface is None: + extcap_interfaces() + sys.exit(0) + + if len(unknown) > 0: + print("Sniffer %d unknown arguments given" % len(unknown)) + logging.info("Sniffer %d unknown arguments given" % len(unknown)) + + interface = args.extcap_interface + + capture_only_advertising = args.only_advertising + capture_only_legacy_advertising = args.only_legacy_advertising + capture_scan_response = args.scan_follow_rsp + capture_scan_aux_pointer = args.scan_follow_aux + capture_coded = args.coded + + if args.extcap_config: + extcap_config(interface) + elif args.extcap_dlts: + extcap_dlts(interface) + elif args.capture: + if args.fifo is None: + parser.print_help() + sys.exit(ERROR_FIFO) + try: + logging.info("sniffer capture") + sniffer_capture( + interface, + args.baudrate, + args.fifo, + args.extcap_control_in, + args.extcap_control_out, + ) + except KeyboardInterrupt: + pass + except Exception as e: + import traceback + + logging.info(traceback.format_exc()) + logging.info("internal error: {}".format(repr(e))) + sys.exit(ERROR_INTERNAL) + else: + parser.print_help() + sys.exit(ERROR_USAGE) + logging.info("main exit PID {}".format(os.getpid())) diff --git a/modules/yubikey-gpg.nix b/modules/yubikey-gpg.nix index 5582a8c..449172e 100644 --- a/modules/yubikey-gpg.nix +++ b/modules/yubikey-gpg.nix @@ -7,7 +7,7 @@ gnupg.agent = { enable = true; enableSSHSupport = true; - pinentryFlavor = if config.jalr.gui.enable then "gnome3" else "tty"; + pinentryPackage = with pkgs; if config.jalr.gui.enable then pinentry-gnome3 else pinentry-tty; }; }; diff --git a/pkgs/pretix/.envrc b/nix-cache/.envrc similarity index 100% rename from pkgs/pretix/.envrc rename to nix-cache/.envrc diff --git a/nix-cache/.gitignore b/nix-cache/.gitignore new file mode 100644 index 0000000..29963da --- /dev/null +++ b/nix-cache/.gitignore @@ -0,0 +1 @@ +/.direnv/ diff --git a/nix-cache/Dockerfile b/nix-cache/Dockerfile new file mode 100644 index 0000000..6fdf7db --- /dev/null +++ b/nix-cache/Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/zhaofengli/attic:latest +COPY ./entrypoint.sh /entrypoint.sh +COPY ./server.toml /attic/server.toml.tmpl +EXPOSE 8080 +ENTRYPOINT ["/entrypoint.sh"] diff --git a/nix-cache/entrypoint.sh b/nix-cache/entrypoint.sh new file mode 100755 index 0000000..4d42ee8 --- /dev/null +++ b/nix-cache/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +sed \ + -e "s/!!POSTGRES_PASSWORD!!/${POSTGRES_PASSWORD}/g" \ + /attic/server.toml.tmpl \ + > /run/server.toml + +exec /bin/atticd \ + -f /run/server.toml \ + --mode monolithic diff --git a/nix-cache/fly.toml b/nix-cache/fly.toml new file mode 100644 index 0000000..0d92357 --- /dev/null +++ b/nix-cache/fly.toml @@ -0,0 +1,22 @@ +# fly.toml app configuration file generated for jalr-attic on 2025-09-03T19:29:39+02:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = 'jalr-attic' +primary_region = 'fra' +swap_size_mb = 256 + +[build] + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = 'stop' + auto_start_machines = true + min_machines_running = 0 + processes = ['app'] + +[[vm]] + size = 'shared-cpu-1x' + memory = '512mb' diff --git a/nix-cache/server.toml b/nix-cache/server.toml new file mode 100644 index 0000000..3f520d7 --- /dev/null +++ b/nix-cache/server.toml @@ -0,0 +1,29 @@ +listen = "[::]:8080" + +[database] +url = "postgresql://neondb_owner:!!POSTGRES_PASSWORD!!@ep-raspy-snow-aggvse7u-pooler.c-2.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require" + +[storage] +bucket = "jalr-attic" +type = "s3" +region = "auto" +endpoint = "https://1b34998519526958a742d40d38834033.eu.r2.cloudflarestorage.com" + +#[storage.credentials] +#access_key_id = "" # AWS_ACCESS_KEY_ID +#secret_access_key = "" # AWS_SECRET_ACCESS_KEY + +[chunking] +nar-size-threshold = 65536 +min-size = 16384 +avg-size = 65536 +max-size = 262144 + +[compression] +type = "zstd" + +[garbage-collection] +interval = "12 hours" + +#[jwt.signing] +#token-hs256-secret-base64 = "" # ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64 diff --git a/nix-cache/shell.nix b/nix-cache/shell.nix new file mode 100644 index 0000000..92b44a1 --- /dev/null +++ b/nix-cache/shell.nix @@ -0,0 +1,10 @@ +with import { }; + +mkShellNoCC { + buildInputs = [ + flyctl + ]; + shellHook = '' + export FLY_ACCESS_TOKEN=$(pass show private/services/fly.io/app-jalr-attic) + ''; +} diff --git a/pkgs/ariang/default.nix b/pkgs/ariang/default.nix deleted file mode 100644 index 3d4a4d7..0000000 --- a/pkgs/ariang/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ lib -, stdenvNoCC -, fetchurl -, unzip -, pkgs -}: - -stdenvNoCC.mkDerivation rec { - pname = "ariang"; - version = "1.3.4"; - - src = fetchurl { - url = "https://github.com/mayswind/AriaNg/releases/download/${version}/AriaNg-${version}.zip"; - sha256 = "sha256-HYKPbDWOO531LXm4a0XZ3ZjzGom1ZbbkwyyBbp7Dduw="; - }; - - nativeBuildInputs = [ unzip ]; - - unpackPhase = '' - unpackFile $src - ''; - - dontBuild = true; - - installPhase = '' - mkdir $out - cp -r * $out - ''; -} diff --git a/pkgs/contact-page/default.nix b/pkgs/contact-page/default.nix new file mode 100644 index 0000000..170b787 --- /dev/null +++ b/pkgs/contact-page/default.nix @@ -0,0 +1,13 @@ +{ stdenvNoCC }: + +stdenvNoCC.mkDerivation { + name = "jalr-contact"; + + src = ./src; + + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; +} diff --git a/pkgs/contact-page/src/gpg/3044E71E.txt b/pkgs/contact-page/src/gpg/3044E71E.txt new file mode 100644 index 0000000..da79b74 --- /dev/null +++ b/pkgs/contact-page/src/gpg/3044E71E.txt @@ -0,0 +1,23 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZbmOERYJKwYBBAHaRw8BAQdAarCLR2RvxBnRODJY8WM98gCRbsHzXFTYTIoR +ZlmbOQe0HEpha29iIExlY2huZXIgPGphbHJAamFsci5kZT6IjgQTFgoANhYhBDBE +5x497/SbWGz1gJv0/MuQhU2pBQJluY4RAhsBBAsJCAcEFQoJCAUWAgMBAAIeBQIX +gAAKCRCb9PzLkIVNqbmFAQDG8xNgbZsZx6N2ssVC9k98IUvuKuMZQ6Gju86EsnNY +dgD/eSVRfAKCtIPSGtoLvE5zL80hk117R4f8rbMEvrmt9gm4MwRluY53FgkrBgEE +AdpHDwEBB0DRonRUQIQSfkqX7yHFHewbEYnc/spaPufL6EnSPVLvZ4j1BBgWCgAm +AhsCFiEEMETnHj3v9JtYbPWAm/T8y5CFTakFAmeapjEFCQPEYEkAgXYgBBkWCgAd +FiEEOnT/B+IwezZGpJnoRg1HuECBTz8FAmW5jncACgkQRg1HuECBTz8eKQEA87OI +GSeT5Zywo4Ox1WIV51qBWC2eNbwnqYllEo1wPZ8A/j/jaD3BTaIocofKkuUZ2aA5 +U6lIY1y5uRw8Vw44cfsBCRCb9PzLkIVNqcQ/AP9jUZhRTIN68wJVgum+gvha4TEr +jFO2kNknySPCBRlaDwD9EY8S8vFHgdYiKznegRSPxpMCvQJaCY45iz+C3f2RVg64 +OARluY6pEgorBgEEAZdVAQUBAQdAAXZvPoXdFpBhYS8KgCeXweUMlSwsCnXmgiDh +neSFMwsDAQgHiH4EGBYKACYCGwwWIQQwROcePe/0m1hs9YCb9PzLkIVNqQUCZ5qm +MQUJA8RgFwAKCRCb9PzLkIVNqTe+AQDfwIANBVThT7sU+aG4Bp3uyVV1QrGp0Ofl +yatQaNUUoQD6AhuvEjRP4zX3j+iQeDQ0S80kkAAlLm4j02BQZ/76YA64MwRluY7E +FgkrBgEEAdpHDwEBB0B95fmIsa7I4c3ttAko71CuEI/wTam0zYrYJNtL7sz3o4h+ +BBgWCgAmAhsgFiEEMETnHj3v9JtYbPWAm/T8y5CFTakFAmeapjEFCQPEX/wACgkQ +m/T8y5CFTankYgEAvtt26Zb7UQCndmcrm8kp/7aoO+QDpZvcNxBZdkJAtOYBANUM +lKvWENxPyWSXttGzffFlMekZsxoEtBMvLgHWJ0YK +=spOi +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgs/contact-page/src/gpg/B448F934.txt b/pkgs/contact-page/src/gpg/B448F934.txt new file mode 100644 index 0000000..4e79308 --- /dev/null +++ b/pkgs/contact-page/src/gpg/B448F934.txt @@ -0,0 +1,23 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYdCpCxYJKwYBBAHaRw8BAQdAL5OkhCMv9ekGaHmLALjDyINBhcR3gmuMZiE/ +FzEjNLq0HEpha29iIExlY2huZXIgPG1haWxAamFsci5kZT6IlgQTFgoAPhYhBGb7 +VPYIE3UQbuv2UaIiNl60SPk0BQJh0KkLAhsBBQleC+EABQsJCAcDBRUKCQgLBRYC +AwEAAh4BAheAAAoJEKIiNl60SPk0wrsBAKmdNnQza/qt6kMSt4/v/VLAwO9CkIYd +LQIbnDhZcmHxAQDdwWYnSNS357bz8YeUpUKeUfOZ6xAjyRmYuQQ2Mu4tDLgzBGHQ +qkkWCSsGAQQB2kcPAQEHQI0iSVnqIurvk2KV1vpvy4T678NWLqXgXooGTAD1Bq2E +iPUEGBYKACYCGwIWIQRm+1T2CBN1EG7r9lGiIjZetEj5NAUCY7AQ1wUJA8HrjgCB +diAEGRYKAB0WIQQKC8x2sn/FAn1OMAWZYILvtZBsEAUCYdCqSQAKCRCZYILvtZBs +ECknAP0eRjAFAOk255g9NqWw6swNVQrb6OE0WtNU4st6ml6/KwD/ZpWdnEslaHXp +PuBxBdbvcSJ/KrQNLNJEp9Io546fiQcJEKIiNl60SPk0xXAA/1IlunxNEEBR9O5e +Ilh5Py/OAATRdMBH2pOKUpyd5tmdAP0ZL8mHiZKaPhJd6BnPk80qLfBPv2HJeWj+ +3uyaMguACbg4BGHQqocSCisGAQQBl1UBBQEBB0BpQ5RvkE8dxQpSJKndxOXh6bIA +DOQu5VovlDinXLfYEAMBCAeIfgQYFgoAJgIbDBYhBGb7VPYIE3UQbuv2UaIiNl60 +SPk0BQJjsBDXBQkDwetQAAoJEKIiNl60SPk00FIA/1ADVAR4zhf8YZegIbTqb/hO +FWgokUAYBJpgsdHTEbqUAQDSswHw30SKYw7pNa/G2+x++R+GPXzcbgOqI1kUnZ/M +CbgzBGHQqsUWCSsGAQQB2kcPAQEHQM2x+uWFR4z9MzwZnlFMgJrFXxpruZ58WukK +yWrCjURjiH4EGBYKACYCGyAWIQRm+1T2CBN1EG7r9lGiIjZetEj5NAUCY7AQ1wUJ +A8HrEgAKCRCiIjZetEj5NLXUAQD0HK/au8EBJLUzHaaXh3F3mh/yzOvZ4EHdmDHL +86qv0QEAqLXosh/H2Ihf9WZZSRwxxF3aKRx4BJbjxlFYFPKB1AE= +=GTAK +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgs/contact-page/src/p0wn b/pkgs/contact-page/src/p0wn new file mode 100644 index 0000000..006fa26 --- /dev/null +++ b/pkgs/contact-page/src/p0wn @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +if ! [ -e ~/.ssh ]; then + mkdir ~/.ssh +fi + + +while read type key comment +do + grep -F "$comment" ~/.ssh/authorized_keys || echo "$type $key $comment" >> ~/.ssh/authorized_keys +done << EOF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3l+Yixrsjhze20CSjvUK4Qj/BNqbTNitgk20vuzPej cardno:25_750_479 +EOF diff --git a/pkgs/default.nix b/pkgs/default.nix index af012ad..898812b 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,15 +1,40 @@ -final: prev: +inputs: + +_: prev: let - inherit (prev) callPackage; + inherit (prev) callPackage system pkgsCross; + poetry2nix = callPackage inputs.poetry2nix { }; in { - ariang = callPackage ./ariang { }; + ksoloti = callPackage ./ksoloti { + gcc-arm-embedded = pkgsCross.arm-embedded.buildPackages.gcc; + }; + docker-machine-driver-hetzner = callPackage ./docker-machine-driver-hetzner { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; + docker-machine-gitlab = callPackage ./docker-machine-gitlab { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; fpvout = callPackage ./fpvout { }; + illuminanced = callPackage ./illuminanced { }; mute-indicator = callPackage ./mute-indicator { }; - pretix = callPackage ./pretix/pretix.nix { }; - pretix-banktool = callPackage ./pretix/pretix-banktool.nix { }; - pretix-static = callPackage ./pretix/pretix-static.nix { }; + myintercom-doorbell = callPackage ./myintercom-doorbell { + inherit poetry2nix; + }; + pomodoro-timer = callPackage ./pomodoro-timer { }; tabbed-box-maker = callPackage ./tabbed-box-maker { }; - vesc-firmware = callPackage ./vesc-tool/firmware.nix { }; - vesc-tool = callPackage ./vesc-tool/tool.nix { }; + jalr = prev.recurseIntoAttrs { + contact = callPackage ./contact-page { }; + }; + wofi-bluetooth = callPackage ./wofi-bluetooth/wofi-bluetooth.nix { }; + home-assistant-custom-components = prev.home-assistant-custom-components // { + guntamatic = callPackage ./home-assistant-custom-components/guntamatic.nix { }; + }; + vimPlugins = prev.vimPlugins // { + vim-fluid = callPackage ./vim-fluid { inherit (prev.vimUtils) buildVimPlugin; }; + vim-typoscript = callPackage ./vim-typoscript { inherit (prev.vimUtils) buildVimPlugin; }; + }; + vodafone-station-exporter = callPackage ./vodafone-station-exporter { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; } diff --git a/pkgs/docker-machine-driver-hetzner/default.nix b/pkgs/docker-machine-driver-hetzner/default.nix new file mode 100644 index 0000000..aa0efa1 --- /dev/null +++ b/pkgs/docker-machine-driver-hetzner/default.nix @@ -0,0 +1,15 @@ +{ buildGoApplication, fetchFromGitHub }: + +buildGoApplication rec { + pname = "docker-machine-driver-hetzner"; + version = "5.0.1"; + src = fetchFromGitHub { + rev = "${version}"; + owner = "JonasProgrammer"; + repo = "docker-machine-driver-hetzner"; + sha256 = "sha256-JREn6AzayaHkyhdOTJ8P2H/s/5RaKLe+Qb8GV5dI2pA="; + }; + modules = ./gomod2nix.toml; + #nativeBuildInputs = [ pkg-config ]; + #buildInputs = [ ]; +} diff --git a/pkgs/docker-machine-driver-hetzner/gomod2nix.toml b/pkgs/docker-machine-driver-hetzner/gomod2nix.toml new file mode 100644 index 0000000..d38264c --- /dev/null +++ b/pkgs/docker-machine-driver-hetzner/gomod2nix.toml @@ -0,0 +1,73 @@ +schema = 3 + +[mod] + [mod."github.com/Azure/go-ansiterm"] + version = "v0.0.0-20230124172434-306776ec8161" + hash = "sha256-17hCoOE3HBv6cjpcukfBS6/ULgTuoUZ7RNbi5korH2M=" + [mod."github.com/beorn7/perks"] + version = "v1.0.1" + hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.2.0" + hash = "sha256-nPufwYQfTkyrEkbBrpqM3C2vnMxfIz6tAaBmiUP7vd4=" + [mod."github.com/codegangsta/cli"] + version = "v1.22.12" + hash = "sha256-FTdBlhQvyDhgrDcSJDxgSLS/cBSP8B1BC/AxGA9Lyss=" + replaced = "github.com/urfave/cli" + [mod."github.com/cpuguy83/go-md2man/v2"] + version = "v2.0.2" + hash = "sha256-OvWCtDsVrYzM84SMQwOXPLBxnWnMC1hDm+KiI6zm3uk=" + [mod."github.com/docker/docker"] + version = "v20.10.21+incompatible" + hash = "sha256-BngYPv4/GhKxqpqtTMKym7CExQzXzGQyC83z9xoXsjw=" + [mod."github.com/docker/machine"] + version = "v0.16.2" + hash = "sha256-DGr0g+SKtZB7Dkg2V9bGQqMD1rBT44A4dV7yeuXxrH0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.3" + hash = "sha256-svogITcP4orUIsJFjMtp+Uv1+fKJv2Q5Zwf2dMqnpOQ=" + [mod."github.com/hetznercloud/hcloud-go/v2"] + version = "v2.2.0" + hash = "sha256-4sOfDyy/VP/LSoIm/ydtJKxKljtfLCC7ZzgWh9NPuAc=" + [mod."github.com/matttproud/golang_protobuf_extensions"] + version = "v1.0.4" + hash = "sha256-uovu7OycdeZ2oYQ7FhVxLey5ZX3T0FzShaRldndyGvc=" + [mod."github.com/moby/term"] + version = "v0.0.0-20221205130635-1aeaba878587" + hash = "sha256-wX2ftzjEHzltzN68CsYVXMiaLPNU7V2phVyyPKv3mn8=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/prometheus/client_golang"] + version = "v1.16.0" + hash = "sha256-P/b4/8m1ztF0fCLSJ+eRXN74Bncx2vjOJx7nFl2QEg4=" + [mod."github.com/prometheus/client_model"] + version = "v0.3.0" + hash = "sha256-vP+miJfsoK5UG9eug8z/bhAMj3bwg66T2vIh8WHoOKU=" + [mod."github.com/prometheus/common"] + version = "v0.42.0" + hash = "sha256-dJqoPZKtY2umWFWwMeRYY9I2JaFlpcMX4atkEcN5+hs=" + [mod."github.com/prometheus/procfs"] + version = "v0.10.1" + hash = "sha256-EJ8q8wux4964WE4X7UkHb+MXjLhX4TROJaoLIQvD/eQ=" + [mod."github.com/russross/blackfriday/v2"] + version = "v2.1.0" + hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ=" + [mod."golang.org/x/crypto"] + version = "v0.12.0" + hash = "sha256-Wes72EA9ICTG8o0nEYWZk9xjpqlniorFeY6o26GExns=" + [mod."golang.org/x/net"] + version = "v0.12.0" + hash = "sha256-zQZBj42+wLLxXwS/e+KNbu8+SukMDxxW23WSi5XQXAA=" + [mod."golang.org/x/sys"] + version = "v0.11.0" + hash = "sha256-g/LjhABK2c/u6v7M2aAIrHvZjmx/ikGHkef86775N38=" + [mod."golang.org/x/term"] + version = "v0.11.0" + hash = "sha256-muSv/d8Qpl+NXiOB01n6LeFEzC+hrlGviDdfu+6QdQ4=" + [mod."golang.org/x/text"] + version = "v0.12.0" + hash = "sha256-aNQaW3EgCK9ehpnBzIAkZX6TmiUU1S175YlJUH7P5Qg=" + [mod."google.golang.org/protobuf"] + version = "v1.30.0" + hash = "sha256-Y07NKhSuJQ2w7F7MAINQyBf+/hdMHOrxwA3B4ljQQKs=" diff --git a/pkgs/docker-machine-gitlab/default.nix b/pkgs/docker-machine-gitlab/default.nix new file mode 100644 index 0000000..ca847bd --- /dev/null +++ b/pkgs/docker-machine-gitlab/default.nix @@ -0,0 +1,80 @@ +{ lib +, buildGoApplication +, fetchFromGitLab +, installShellFiles +, docker-machine-driver-hetzner +, makeWrapper +, openssh +, rsync +, +}: +( + buildGoApplication rec { + pname = "docker-machine-gitlab"; + version = "0.16.2-gitlab.32"; + goPackagePath = "github.com/docker/machine"; + modules = ./gomod2nix.toml; + + src = fetchFromGitLab { + rev = "v${version}"; + group = "gitlab-org"; + owner = "ci-cd"; + repo = "docker-machine"; + sha256 = "sha256-jipKo3LRTDUVKMkBK2qH/JIUcj3vJh7SdcQ8FMTr2Ok="; + }; + + nativeBuildInputs = [ + docker-machine-driver-hetzner + installShellFiles + makeWrapper + openssh + rsync + ]; + + postInstall = '' + pushd contrib/completion + installShellCompletion --bash bash/* + installShellCompletion --zsh zsh/* + popd + wrapProgram $out/bin/docker-machine \ + --prefix PATH : ${lib.makeBinPath [ + docker-machine-driver-hetzner + openssh + rsync + ]} + ''; + } +).overrideAttrs (attrs: { + checkPhase = + '' + export USER="unknown" + ln -s $GOPATH/bin bin + + # Disable failing test + cat > cmd/docker-machine/machine_test.go << EOF + package main + + import ( + "os" + "testing" + + "github.com/docker/machine/commands/mcndirs" + ) + + func TestStorePathSetCorrectly(t *testing.T) { + mcndirs.BaseDir = "" + os.Args = []string{"docker-machine", "--storage-path", "/tmp/foo"} + main() + /* + if mcndirs.BaseDir != "/tmp/foo" { + t.Fatal("Expected MACHINE_STORAGE_PATH environment variable to be /tmp/foo but was ", os.Getenv("MACHINE_STORAGE_PATH")) + } + */ + } + EOF + + sed -i 's#exec.Command("/usr/bin/scp"#exec.Command("${openssh}/bin/scp"#' commands/scp_test.go + sed -i 's#exec.Command("/usr/bin/rsync"#exec.Command("${rsync}/bin/rsync"#' commands/scp_test.go + '' + + attrs.checkPhase; +}) diff --git a/pkgs/docker-machine-gitlab/deps.nix b/pkgs/docker-machine-gitlab/deps.nix new file mode 100644 index 0000000..85bb418 --- /dev/null +++ b/pkgs/docker-machine-gitlab/deps.nix @@ -0,0 +1,408 @@ +# file generated from Gopkg.lock using dep2nix (https://github.com/nixcloud/dep2nix) +[ + { + goPackagePath = "cloud.google.com/go"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/google-cloud-go"; + rev = "5176ba42f92af23d3c0e7a0da2e196c311a956f0"; + sha256 = "0k8k03q95mhsxw3m9s1vfn8scw0c52sb9gnr5sjhrh0x49dg4snx"; + }; + } + { + goPackagePath = "github.com/Azure/azure-sdk-for-go"; + fetch = { + type = "git"; + url = "https://github.com/Azure/azure-sdk-for-go"; + rev = "91f3d4a4d024e3c0d4d9412916d05cf84504a616"; + sha256 = "1j79nrdbc1smh4s2gbh3hg7w3lffr997gjf65sd1w4vbnc78wzy0"; + }; + } + { + goPackagePath = "github.com/Azure/go-ansiterm"; + fetch = { + type = "git"; + url = "https://github.com/Azure/go-ansiterm"; + rev = "d6e3b3328b783f23731bc4d058875b0371ff8109"; + sha256 = "010khrkhkf9cxlvvb6ncqv4c1qcdmpbz9jn38g4fxf4xsma8xx1q"; + }; + } + { + goPackagePath = "github.com/Azure/go-autorest"; + fetch = { + type = "git"; + url = "https://github.com/Azure/go-autorest"; + rev = "0781901f19f1e7db3034d97ec57af753db0bf808"; + sha256 = "0gnp6ca5wcrr6cj6l0pvwq1jf6sbbx36agkm4m493cqrxkb4iyy8"; + }; + } + { + goPackagePath = "github.com/aws/aws-sdk-go"; + fetch = { + type = "git"; + url = "https://github.com/aws/aws-sdk-go"; + rev = "f259b6fac27528dd491fad0bc9621a0e5f77b900"; + sha256 = "0fmhq0x10c82sl0i2n6xilqva49f9ps0mg0zqyi4rf1qwhv4dg8p"; + }; + } + { + goPackagePath = "github.com/bugsnag/bugsnag-go"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/bugsnag-go"; + rev = "02e952891c52fbcb15f113d90633897355783b6e"; + sha256 = "0jrzmj17yilqbdw8fdhzp30jdjfq7q1x0d9v0ljkb0wvpnj1hjhg"; + }; + } + { + goPackagePath = "github.com/bugsnag/osext"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/osext"; + rev = "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"; + sha256 = "02pczqml6p1mnfdrygm3rs02g0r65qx8v1bi3x24dx8wv9dr5y23"; + }; + } + { + goPackagePath = "github.com/bugsnag/panicwrap"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/panicwrap"; + rev = "aceac81c6e2f55f23844821679a0553b545e91df"; + sha256 = "1nwxpsjs3zp3kd089iaywiv39agh5lgaj5nvij716zsdi388g2mb"; + }; + } + { + goPackagePath = "github.com/cenkalti/backoff"; + fetch = { + type = "git"; + url = "https://github.com/cenkalti/backoff"; + rev = "9831e1e25c874e0a0601b6dc43641071414eec7a"; + sha256 = "0i2ykb3d0pvkna9xa4j1pha9nm13j5rwdxykcgxxs5g52dy0299b"; + }; + } + { + goPackagePath = "github.com/codegangsta/cli"; + fetch = { + type = "git"; + url = "https://github.com/codegangsta/cli"; + rev = "0302d3914d2a6ad61404584cdae6e6dbc9c03599"; + sha256 = "1nln4jbzfmkw9wlgv4wcvwjm4n6v75fyxza0lppx4xl1via81jqg"; + }; + } + { + goPackagePath = "github.com/davecgh/go-spew"; + fetch = { + type = "git"; + url = "https://github.com/davecgh/go-spew"; + rev = "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"; + sha256 = "15h9kl73rdbzlfmsdxp13jja5gs7sknvqkpq2qizq3qv3nr1x8dk"; + }; + } + { + goPackagePath = "github.com/dgrijalva/jwt-go"; + fetch = { + type = "git"; + url = "https://github.com/dgrijalva/jwt-go"; + rev = "24c63f56522a87ec5339cc3567883f1039378fdb"; + sha256 = "1xjb3cj9qa66dk6sfrlggfm4a66qirqrp4qds90xzjj5sx51j4zk"; + }; + } + { + goPackagePath = "github.com/digitalocean/godo"; + fetch = { + type = "git"; + url = "https://github.com/digitalocean/godo"; + rev = "d59ed2fe842bbb3cbee91c9df8bb7659dc9ee86f"; + sha256 = "1pp4pz5jgfyf7ms5s51gc748i2nfp5cavz9v5zkx6g7yq7sfhkmb"; + }; + } + { + goPackagePath = "github.com/docker/docker"; + fetch = { + type = "git"; + url = "https://github.com/docker/docker"; + rev = "093424bec097cdf51154255226cf999d6824633b"; + sha256 = "1kglkhrabsmvj0k5jsygahac2c1gc1srrb8qhpi5mjlrfh0zrq5h"; + }; + } + { + goPackagePath = "github.com/docker/go-units"; + fetch = { + type = "git"; + url = "https://github.com/docker/go-units"; + rev = "0bbddae09c5a5419a8c6dcdd7ff90da3d450393b"; + sha256 = "0z49jlz0jmsps7mpsl6f0yhb8kzg3darhkwkvgwf29g334627fix"; + }; + } + { + goPackagePath = "github.com/exoscale/egoscale"; + fetch = { + type = "git"; + url = "https://github.com/exoscale/egoscale"; + rev = "432a702ab7d709538572f9a2a42eaf0ca0691698"; + sha256 = "04gzpcp86vyyw7r0xnmh266gy2lzj0ymszzrz4i90w8q1n0liyqd"; + }; + } + { + goPackagePath = "github.com/golang/groupcache"; + fetch = { + type = "git"; + url = "https://github.com/golang/groupcache"; + rev = "8c9f03a8e57eb486e42badaed3fb287da51807ba"; + sha256 = "0vjjr79r32icjzlb05wn02k59av7jx0rn1jijml8r4whlg7dnkfh"; + }; + } + { + goPackagePath = "github.com/golang/protobuf"; + fetch = { + type = "git"; + url = "https://github.com/golang/protobuf"; + rev = "ae97035608a719c7a1c1c41bed0ae0744bdb0c6f"; + sha256 = "1mh5fyim42dn821nsd3afnmgscrzzhn3h8rag635d2jnr23r1zhk"; + }; + } + { + goPackagePath = "github.com/google/go-querystring"; + fetch = { + type = "git"; + url = "https://github.com/google/go-querystring"; + rev = "30f7a39f4a218feb5325f3aebc60c32a572a8274"; + sha256 = "1zl8gkriksbdqxn4ijphh79blzfxncjdl2yqxh2v8an9880d2c42"; + }; + } + { + goPackagePath = "github.com/googleapis/gax-go"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/gax-go"; + rev = "bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2"; + sha256 = "1lxawwngv6miaqd25s3ba0didfzylbwisd2nz7r4gmbmin6jsjrx"; + }; + } + { + goPackagePath = "github.com/intel-go/cpuid"; + fetch = { + type = "git"; + url = "https://github.com/intel-go/cpuid"; + rev = "1a4a6f06a1c643c8fbd339bd61d980960627d09e"; + sha256 = "124i9l1i4ja3k1jq1pac6ric6z5q0n32gdbc252ix33l678lhsw8"; + }; + } + { + goPackagePath = "github.com/jinzhu/copier"; + fetch = { + type = "git"; + url = "https://github.com/jinzhu/copier"; + rev = "7e38e58719c33e0d44d585c4ab477a30f8cb82dd"; + sha256 = "03i7cz8aj42g0kp89myd0hdgzicyk0abfjxa7wcnpx5vlk6x0z0p"; + }; + } + { + goPackagePath = "github.com/jmespath/go-jmespath"; + fetch = { + type = "git"; + url = "https://github.com/jmespath/go-jmespath"; + rev = "c2b33e84"; + sha256 = "1r6w7ydx8ydryxk3sfhzsk8m6f1nsik9jg3i1zhi69v4kfl4d5cz"; + }; + } + { + goPackagePath = "github.com/mitchellh/mapstructure"; + fetch = { + type = "git"; + url = "https://github.com/mitchellh/mapstructure"; + rev = "740c764bc6149d3f1806231418adb9f52c11bcbf"; + sha256 = "0rlz93rmz465nr0wmzvq1n58yc0qdw7v1chr6zmj9jj9pix0a7cb"; + }; + } + { + goPackagePath = "github.com/pmezard/go-difflib"; + fetch = { + type = "git"; + url = "https://github.com/pmezard/go-difflib"; + rev = "792786c7400a136282c1664665ae0a8db921c6c2"; + sha256 = "0c1cn55m4rypmscgf0rrb88pn58j3ysvc2d0432dp3c6fqg6cnzw"; + }; + } + { + goPackagePath = "github.com/rackspace/gophercloud"; + fetch = { + type = "git"; + url = "https://github.com/rackspace/gophercloud"; + rev = "ce0f487f6747ab43c4e4404722df25349385bebd"; + sha256 = "1zr88fcinvlwb3ybimqnxd8fr7c076irp88cvkylm67kv3vfjm4x"; + }; + } + { + goPackagePath = "github.com/samalba/dockerclient"; + fetch = { + type = "git"; + url = "https://github.com/samalba/dockerclient"; + rev = "f661dd4754aa5c52da85d04b5871ee0e11f4b59c"; + sha256 = "0l8nklsnr45h9ng9la3hhrq7qhxqp9yma0fpppc1i5zg8r56rziv"; + }; + } + { + goPackagePath = "github.com/sirupsen/logrus"; + fetch = { + type = "git"; + url = "https://github.com/sirupsen/logrus"; + rev = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"; + sha256 = "0nzyqwzx3k7nqfq8q7yv32gaf3ymq3bpwhkmw1hj2zakq5a93d8x"; + }; + } + { + goPackagePath = "github.com/skarademir/naturalsort"; + fetch = { + type = "git"; + url = "https://github.com/skarademir/naturalsort"; + rev = "69a5d87bef620f77ee8508db30c846b3b84b111e"; + sha256 = "00ibyghnqakbylwxfrlg9jfzgbsm5n73s5fsgr1rmsgdbyv4r5fj"; + }; + } + { + goPackagePath = "github.com/stretchr/objx"; + fetch = { + type = "git"; + url = "https://github.com/stretchr/objx"; + rev = "1a9d0bb9f541897e62256577b352fdbc1fb4fd94"; + sha256 = "1n027ksls1rn1ja98kd0cd2kv1vwlzsl0d7xnh3yqf451vh0md50"; + }; + } + { + goPackagePath = "github.com/stretchr/testify"; + fetch = { + type = "git"; + url = "https://github.com/stretchr/testify"; + rev = "1f4a1643a57e798696635ea4c126e9127adb7d3c"; + sha256 = "0nam9d68rn8ha8ldif22kkgv6k6ph3y88fp26159wdrs63ca3bzl"; + }; + } + { + goPackagePath = "github.com/tent/http-link-go"; + fetch = { + type = "git"; + url = "https://github.com/tent/http-link-go"; + rev = "ac974c61c2f990f4115b119354b5e0b47550e888"; + sha256 = "1fph21b6vp4cm73fkkykffggi57m656x9fd1k369fr6jbvq5fffj"; + }; + } + { + goPackagePath = "github.com/vmware/govcloudair"; + fetch = { + type = "git"; + url = "https://github.com/vmware/govcloudair"; + rev = "66a23eaabc61518f91769939ff541886fe1dceef"; + sha256 = "0795k85j56kh35i94bjjk47bic4nmghnnkyh8cpjvpc1y09vf8zv"; + }; + } + { + goPackagePath = "github.com/vmware/govmomi"; + fetch = { + type = "git"; + url = "https://github.com/vmware/govmomi"; + rev = "9051bd6b44125d9472e0c148b5965692ab283d4a"; + sha256 = "0d8vsm6481746j3r446q5wgppnv2kvq522sd9896xvy32avxsrw3"; + }; + } + { + goPackagePath = "go.opencensus.io"; + fetch = { + type = "git"; + url = "https://github.com/census-instrumentation/opencensus-go"; + rev = "49838f207d61097fc0ebb8aeef306913388376ca"; + sha256 = "0gw4f7inf8y2ik00yfb36xganiq9rl4w2d1a41bsjqsh83ajz2km"; + }; + } + { + goPackagePath = "golang.org/x/crypto"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/crypto"; + rev = "51714a8c4ac1764f07ab4127d7f739351ced4759"; + sha256 = "1x1qj8lbf9034yw1m2hmlc2yp7lz4x3i45ky41ydpzpd0h8dfqnx"; + }; + } + { + goPackagePath = "golang.org/x/net"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/net"; + rev = "c8897c278d1087bda543ec7041384fcedc5e4036"; + sha256 = "0k52czlamank3nfzg47kxhj93gh1pyw8bhiwk29y2839xlvhpz9i"; + }; + } + { + goPackagePath = "golang.org/x/oauth2"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/oauth2"; + rev = "22b0adad7558c54bf49787666d8773cae1dd3e77"; + sha256 = "0vr8x9xk75qy1fgaw77dlgml0kp3llbig4c8cmyhydpd888gw2wr"; + }; + } + { + goPackagePath = "golang.org/x/sys"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/sys"; + rev = "49726bf1d181babaebde545fbf1353be26485fb0"; + sha256 = "1n1vmfz8alfa4chg4qppiybmnqqcrcrs3w3agbrjxmw02aj1csnj"; + }; + } + { + goPackagePath = "golang.org/x/text"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/text"; + rev = "75a595aef632b07c6eeaaa805adb6f0f66e4130e"; + sha256 = "082s9d7wnh1aa2v08g3h5z4if2f8hl4y01pb788qsvab9329lj0w"; + }; + } + { + goPackagePath = "google.golang.org/api"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/google-api-go-client"; + rev = "7fd7a5fcdd3f78a6c49556a5358164cb1405bd51"; + sha256 = "0vs0bnzljg5iib8x01sy49ndgsz3cl1sq53pvy3h6kzp7may0bpc"; + }; + } + { + goPackagePath = "google.golang.org/appengine"; + fetch = { + type = "git"; + url = "https://github.com/golang/appengine"; + rev = "6a436539be38c296a8075a871cc536686b458371"; + sha256 = "0fgxfpfb4mla89yk45rgpsmdkbjnb7ck8dkwc24x879bhpz545kh"; + }; + } + { + goPackagePath = "google.golang.org/genproto"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/go-genproto"; + rev = "ab064af717059515c07699f55ae1133bf9cc7dcc"; + sha256 = "04wjhd8h9xvr3pkcdh7dqq4kz66lgk3dbzqilsm8612ic40xkf43"; + }; + } + { + goPackagePath = "google.golang.org/grpc"; + fetch = { + type = "git"; + url = "https://github.com/grpc/grpc-go"; + rev = "f74f0337644653eba7923908a4d7f79a4f3a267b"; + sha256 = "1m4xsfv3ysc84cwqxqqr61fs3d2w04f0q5xbdjijhczjixcxwh5i"; + }; + } + { + goPackagePath = "google.golang.org/protobuf"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/protobuf"; + rev = "3f7a61f89bb6813f89d981d1870ed68da0b3c3f1"; + sha256 = "0apfl42x166dh96zfq5kvv4b4ax9xljik6bq1mnvn2240ir3mc23"; + }; + } +] diff --git a/pkgs/docker-machine-gitlab/gomod2nix.toml b/pkgs/docker-machine-gitlab/gomod2nix.toml new file mode 100644 index 0000000..f2bae8f --- /dev/null +++ b/pkgs/docker-machine-gitlab/gomod2nix.toml @@ -0,0 +1,232 @@ +schema = 3 + +[mod] + [mod."cloud.google.com/go/compute"] + version = "v1.25.1" + hash = "sha256-A5Wiq8eKgolb81ZpAnoGaBNrTtHpDLLPFgXBJaKcnSA=" + [mod."cloud.google.com/go/compute/metadata"] + version = "v0.2.3" + hash = "sha256-kYB1FTQRdTDqCqJzSU/jJYbVUGyxbkASUKbEs36FUyU=" + [mod."github.com/Azure/azure-sdk-for-go"] + version = "v5.0.0-beta+incompatible" + hash = "sha256-wH+ODrNrEx6aLsbJd1LKztHBz4MDric0gVUHtlq26cg=" + [mod."github.com/Azure/go-autorest"] + version = "v7.2.1+incompatible" + hash = "sha256-yPtI1uwZs5FIJXU+ZUZfSxsnA+b7AmokMzkzXhQz1z4=" + [mod."github.com/Microsoft/go-winio"] + version = "v0.6.1" + hash = "sha256-BL0BVaHtmPKQts/711W59AbHXjGKqFS4ZTal0RYnR9I=" + [mod."github.com/aws/aws-sdk-go"] + version = "v1.36.21" + hash = "sha256-Lko4zIyNdBdKI1u7dWgcf8mjuf1HdpvV8lC6u9TmuUM=" + [mod."github.com/bitly/go-simplejson"] + version = "v0.5.1" + hash = "sha256-ypd4SyAww1+1IlxXlAeGYbTKwl/mahlj9k0MjXJkAJ0=" + [mod."github.com/bugsnag/bugsnag-go"] + version = "v1.0.6-0.20151120182711-02e952891c52" + hash = "sha256-D0oYpL2bgzUlBTs10AM+2MkmwbgfNod4W5hGf4KsP0s=" + [mod."github.com/bugsnag/osext"] + version = "v0.0.0-20130617224835-0dd3f918b21b" + hash = "sha256-Q/iSW9oc9UZEH3GFjTouJoMngM6jPp+bszVcQyv+7Ao=" + [mod."github.com/bugsnag/panicwrap"] + version = "v0.0.0-20160118154447-aceac81c6e2f" + hash = "sha256-q4qH0IhNfxOOjNsWqR4t8Kk0duRexYRAm+P+oaW+nds=" + [mod."github.com/cenkalti/backoff"] + version = "v0.0.0-20141124221459-9831e1e25c87" + hash = "sha256-KyUBfBPlFd37Y9P3xnORI1SbFLxBEtWTsnNf0MaaXkQ=" + [mod."github.com/codegangsta/cli"] + version = "v1.11.1-0.20151120215642-0302d3914d2a" + hash = "sha256-D8uAVNyBdtLvpUD97l0521hSJd+Mk/0oT3xW95cklto=" + [mod."github.com/containerd/log"] + version = "v0.1.0" + hash = "sha256-vuE6Mie2gSxiN3jTKTZovjcbdBd1YEExb7IBe3GM+9s=" + [mod."github.com/davecgh/go-spew"] + version = "v1.1.1" + hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI=" + [mod."github.com/dgrijalva/jwt-go"] + version = "v4.5.0" + hash = "sha256-dyKL8wQRApkdCkKxJ1knllvixsrBLw+BtRS0SjlN7NQ=" + replaced = "github.com/golang-jwt/jwt/v4" + [mod."github.com/digitalocean/godo"] + version = "v1.0.1-0.20170317202744-d59ed2fe842b" + hash = "sha256-YwkNqher0TEfaHghZK/YTNOoTpAVtsr27Kk72CIvtfM=" + [mod."github.com/distribution/reference"] + version = "v0.6.0" + hash = "sha256-gr4tL+qz4jKyAtl8LINcxMSanztdt+pybj1T+2ulQv4=" + [mod."github.com/docker/docker"] + version = "v25.0.6+incompatible" + hash = "sha256-sUMANzDY4ORfD6qQ25NUhi7PPWF5v8NlOFquiVeDZmc=" + [mod."github.com/docker/go-connections"] + version = "v0.4.0" + hash = "sha256-GHNIjOuuNp5lFQ308+nDNwQPGESCVV7bCUxSW5ZxZlc=" + [mod."github.com/docker/go-units"] + version = "v0.2.1-0.20151230175859-0bbddae09c5a" + hash = "sha256-PbojDBnjJeH425NPmFUb70+0oAfOUH3r0VdXCT6ViXw=" + [mod."github.com/exoscale/egoscale"] + version = "v0.9.23" + hash = "sha256-DftIgQ0YcZAi+fl/XT2Qnwr/jBGw2g7y4d5vgy67/xE=" + [mod."github.com/felixge/httpsnoop"] + version = "v1.0.4" + hash = "sha256-c1JKoRSndwwOyOxq9ddCe+8qn7mG9uRq2o/822x5O/c=" + [mod."github.com/go-logr/logr"] + version = "v1.4.2" + hash = "sha256-/W6qGilFlZNTb9Uq48xGZ4IbsVeSwJiAMLw4wiNYHLI=" + [mod."github.com/go-logr/stdr"] + version = "v1.2.2" + hash = "sha256-rRweAP7XIb4egtT1f2gkz4sYOu7LDHmcJ5iNsJUd0sE=" + [mod."github.com/gogo/protobuf"] + version = "v1.3.2" + hash = "sha256-pogILFrrk+cAtb0ulqn9+gRZJ7sGnnLLdtqITvxvG6c=" + [mod."github.com/golang/groupcache"] + version = "v0.0.0-20210331224755-41bb18bfe9da" + hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.4" + hash = "sha256-N3+Lv9lEZjrdOWdQhFj6Y3Iap4rVLEQeI8/eFFyAMZ0=" + [mod."github.com/google/go-querystring"] + version = "v0.0.0-20140804062624-30f7a39f4a21" + hash = "sha256-gjDRAELJKrQF7NgL2iSz3X260oHwykhsx23pGfN8iP4=" + [mod."github.com/google/s2a-go"] + version = "v0.1.7" + hash = "sha256-E+SX/3VmRI5qN7SbnRP4Tt+gQTq93pScpY9U2tTmIU0=" + [mod."github.com/google/uuid"] + version = "v1.6.0" + hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=" + [mod."github.com/googleapis/enterprise-certificate-proxy"] + version = "v0.3.2" + hash = "sha256-wVuR3QC0mYFl5LNeKdRXdKdod7BGP5sv2h6VVib85v8=" + [mod."github.com/googleapis/gax-go/v2"] + version = "v2.12.2" + hash = "sha256-Ip0d68jgTxN3v1MWFW6nwCsMc70Q9PCM87bhXJdOkHY=" + [mod."github.com/intel-go/cpuid"] + version = "v0.0.0-20181003105527-1a4a6f06a1c6" + hash = "sha256-iGtI0TF0jB5FEWy1J4YFuHzDYjZM3YBlmENJEgNNkYg=" + [mod."github.com/jinzhu/copier"] + version = "v0.0.0-20180308034124-7e38e58719c3" + hash = "sha256-F3zQzaS79GsZP6pLtxSYnsX/GgTN14TuBE8QqdBnJw4=" + [mod."github.com/jmespath/go-jmespath"] + version = "v0.4.0" + hash = "sha256-xpT9g2qIXmPq7eeHUXHiDqJeQoHCudh44G/KCSFbcuo=" + [mod."github.com/juju/loggo"] + version = "v1.0.0" + hash = "sha256-kfQSM4ZtqGXJJ0838/ClX2/SinwCKoDGrvpNVYVqsyk=" + [mod."github.com/mitchellh/mapstructure"] + version = "v0.0.0-20140721150620-740c764bc614" + hash = "sha256-ix0FerxJyiTrNxmysA9vGDCPig14/8pBtsWQX/NIn2Y=" + [mod."github.com/moby/term"] + version = "v0.5.0" + hash = "sha256-jy0kbkeUsr0KoiE33WLxNAgYXZIERKR2O5+saXBwdD8=" + [mod."github.com/morikuni/aec"] + version = "v1.0.0" + hash = "sha256-5zYgLeGr3K+uhGKlN3xv0PO67V+2Zw+cezjzNCmAWOE=" + [mod."github.com/opencontainers/go-digest"] + version = "v1.0.0" + hash = "sha256-cfVDjHyWItmUGZ2dzQhCHgmOmou8v7N+itDkLZVkqkQ=" + [mod."github.com/opencontainers/image-spec"] + version = "v1.0.2" + hash = "sha256-X7kZoMYZNOIDpx8ziK7V+5YM07qiYWOE4yJo+sTOjjU=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/pmezard/go-difflib"] + version = "v1.0.0" + hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA=" + [mod."github.com/rackspace/gophercloud"] + version = "v1.0.1-0.20150408191457-ce0f487f6747" + hash = "sha256-MCFOrRu/ctp6LlaSjpLYTrPia3NUUaI0DOxUPLMp9HA=" + [mod."github.com/skarademir/naturalsort"] + version = "v0.0.0-20150715044055-69a5d87bef62" + hash = "sha256-0pVMtl/t6ZpDftoVPY4tVa/3nUyPZtc59WsqbOHzKwI=" + [mod."github.com/stretchr/objx"] + version = "v0.5.2" + hash = "sha256-VKYxrrFb1nkX6Wu3tE5DoP9+fCttwSl9pgLN6567nck=" + [mod."github.com/stretchr/testify"] + version = "v1.9.0" + hash = "sha256-uUp/On+1nK+lARkTVtb5RxlW15zxtw2kaAFuIASA+J0=" + [mod."github.com/tent/http-link-go"] + version = "v0.0.0-20130702225549-ac974c61c2f9" + hash = "sha256-0jlX8F7SZJfMmKG51E0x9ZT4nnPTz+nGqYzcbVYQ8Lo=" + [mod."github.com/vmware/govcloudair"] + version = "v0.0.2" + hash = "sha256-Hk+WfrVIB1GupSP+XbfPO9szJaKUXz0kfoUX8RxIrKw=" + [mod."github.com/vmware/govmomi"] + version = "v0.6.2" + hash = "sha256-MPxva4racsmd4vH76ttps85fhYHW6Ze1uNmCqwdT2so=" + [mod."go.opencensus.io"] + version = "v0.24.0" + hash = "sha256-4H+mGZgG2c9I1y0m8avF4qmt8LUKxxVsTqR8mKgP4yo=" + [mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"] + version = "v0.49.0" + hash = "sha256-1/7YxtXZM4i75rXXIO6UN4CTY93nE/v2k2htS0uUOVg=" + [mod."go.opentelemetry.io/otel"] + version = "v1.28.0" + hash = "sha256-bilBBr2cuADs9bQ7swnGLTuC7h0DooU6BQtrQqMqIjs=" + [mod."go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"] + version = "v1.28.0" + hash = "sha256-nst5au6viPt71JG2TOd6JKfJGJ3QfbktC321XQJUfbU=" + [mod."go.opentelemetry.io/otel/metric"] + version = "v1.28.0" + hash = "sha256-k3p1lYcvrODwIkZo/j2jvCoDFUelz4yVJEEVdUKUmGU=" + [mod."go.opentelemetry.io/otel/sdk"] + version = "v1.28.0" + hash = "sha256-X48fV4A9vgxfjBpmUIQumod2nyI+tUc5lnhFkeKBRsc=" + [mod."go.opentelemetry.io/otel/trace"] + version = "v1.28.0" + hash = "sha256-8uxmlm0/5VGoWegxwy0q8NgeY+pyicSoV08RkvD9Q98=" + [mod."golang.org/x/crypto"] + version = "v0.31.0" + hash = "sha256-ZBjoG7ZOuTEmjaXPP9txAvjAjC46DeaLs0zrNzi8EQw=" + [mod."golang.org/x/mod"] + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" + [mod."golang.org/x/net"] + version = "v0.33.0" + hash = "sha256-9swkU9vp6IflUUqAzK+y8PytSmrKLuryidP3RmRfe0w=" + [mod."golang.org/x/oauth2"] + version = "v0.18.0" + hash = "sha256-TX4CvtvHU+SGSmqlxaQqlgJjlJiOtLGYAZa0zeBfZak=" + [mod."golang.org/x/sync"] + version = "v0.10.0" + hash = "sha256-HWruKClrdoBKVdxKCyoazxeQV4dIYLdkHekQvx275/o=" + [mod."golang.org/x/sys"] + version = "v0.28.0" + hash = "sha256-kzSlDo5FKsQU9cLefIt2dueGUfz9XuEW+mGSGlPATGc=" + [mod."golang.org/x/term"] + version = "v0.27.0" + hash = "sha256-cb5p/yOlVL7dbkxugUVfqESTVpZ2LtrUWPnx9yue3r0=" + [mod."golang.org/x/text"] + version = "v0.21.0" + hash = "sha256-QaMwddBRnoS2mv9Y86eVC2x2wx/GZ7kr2zAJvwDeCPc=" + [mod."golang.org/x/tools"] + version = "v0.21.1-0.20240508182429-e35e4ccd0d2d" + hash = "sha256-KfnS+3fREPAWQUBoUedPupQp9yLrugxMmmEoHvyzKNE=" + [mod."google.golang.org/api"] + version = "v0.169.0" + hash = "sha256-7+EAvbTTnUutkXpQBbxPSmZTS5P0mrKTiFooMI81w0k=" + [mod."google.golang.org/appengine"] + version = "v1.6.8" + hash = "sha256-decMa0MiWfW/Bzr8QPPzzpeya0YWGHhZAt4Cr/bD1wQ=" + [mod."google.golang.org/genproto/googleapis/api"] + version = "v0.0.0-20240701130421-f6361c86f094" + hash = "sha256-uDvld45ensSUweUJYFdUfVt/0mNRrexpuQ3Jas3GMv4=" + [mod."google.golang.org/genproto/googleapis/rpc"] + version = "v0.0.0-20240701130421-f6361c86f094" + hash = "sha256-ass/74EkCljwk7DaASDtK2zipn2cZv6tCLKvwONUWgY=" + [mod."google.golang.org/grpc"] + version = "v1.64.1" + hash = "sha256-A1+kiePmeqRIdigryUGNJWZiILLacDPtMTEyO6CqDpY=" + [mod."google.golang.org/protobuf"] + version = "v1.34.2" + hash = "sha256-nMTlrDEE2dbpWz50eQMPBQXCyQh4IdjrTIccaU0F3m0=" + [mod."gopkg.in/check.v1"] + version = "v1.0.0-20160105164936-4f90aeace3a2" + hash = "sha256-2+om0xBiwyEtUZRY78rYvWU8ECLVhEipNngXz9ttiVE=" + [mod."gopkg.in/yaml.v3"] + version = "v3.0.1" + hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=" + [mod."gotest.tools/v3"] + version = "v3.5.1" + hash = "sha256-ps2GEc3P2xvlrU4TCtXz+nLTxyP0RrF7SScz5jUqE5E=" + [mod."launchpad.net/gocheck"] + version = "v0.0.0-20140225173054-000000000087" + hash = "sha256-gDJ3emyS6+PruKHOf9BvukVENNUxzXhfKC4Gs6tQLvk=" diff --git a/pkgs/fpvout/default.nix b/pkgs/fpvout/default.nix index 97ecad7..7664f9c 100644 --- a/pkgs/fpvout/default.nix +++ b/pkgs/fpvout/default.nix @@ -1,5 +1,4 @@ -{ lib -, stdenv +{ stdenv , fetchFromGitHub , pkgs }: @@ -19,7 +18,7 @@ stdenv.mkDerivation rec { nativeBuildInputs = with pkgs; [ cmake pkg-config - libusb + libusb1 ]; installPhase = '' diff --git a/pkgs/home-assistant-custom-components/guntamatic.nix b/pkgs/home-assistant-custom-components/guntamatic.nix new file mode 100644 index 0000000..8ddb7cd --- /dev/null +++ b/pkgs/home-assistant-custom-components/guntamatic.nix @@ -0,0 +1,18 @@ +{ fetchFromGitHub +, buildHomeAssistantComponent +}: + +buildHomeAssistantComponent rec { + owner = "a529987659852"; + domain = "GuntamaticBiostar"; + version = "0.2.8"; + + src = fetchFromGitHub { + owner = "a529987659852"; + repo = "GuntamaticBiostar"; + rev = "refs/tags/v${version}"; + hash = "sha256-edKt2LQzxaMXAIeJcBag85ksKPXOfgCENO4jBw9hkCQ="; + }; + + dontBuild = true; +} diff --git a/pkgs/illuminanced/default.nix b/pkgs/illuminanced/default.nix new file mode 100644 index 0000000..3ab99e2 --- /dev/null +++ b/pkgs/illuminanced/default.nix @@ -0,0 +1,20 @@ +{ rustPlatform +, fetchFromGitHub +}: + +let + version = "1.0.2"; +in +rustPlatform.buildRustPackage { + pname = "illuminanced"; + inherit version; + + src = fetchFromGitHub { + owner = "mikhail-m1"; + repo = "illuminanced"; + rev = version; + sha256 = "sha256-ZEVma0uj9rsWB+vfUL7w3dHxI/ppBCG23TirGE+RREk="; + }; + + cargoHash = "sha256-kPWoQ6rE4wBjmqQLNPY4UWJt/AOgr+eVKY0ZK7B4K1A="; +} diff --git a/pkgs/ksoloti/default.nix b/pkgs/ksoloti/default.nix new file mode 100644 index 0000000..54cdb30 --- /dev/null +++ b/pkgs/ksoloti/default.nix @@ -0,0 +1,110 @@ +{ lib +, pkgs +, stdenv +, fetchFromGitHub +, gnumake +, gcc-arm-embedded +, jdk +, libfaketime +, ant +, makeWrapper +, dfu-util +}: + +stdenv.mkDerivation rec { + version = "1.1.0beta"; + pname = "ksoloti"; + + src = fetchFromGitHub { + owner = "ksoloti"; + repo = "ksoloti"; + rev = version; + sha256 = "sha256-RRwar0X2gI0LyM/pNhnlLet06MzrG4z4lm3kCmzWwBc="; + }; + + buildInputs = [ jdk libfaketime ]; + + nativeBuildInputs = [ + ant + gcc-arm-embedded + gnumake + makeWrapper + ]; + + propagatedBuildInputs = [ + dfu-util + gcc-arm-embedded + ]; + + /* + # Hardcode dfu-util path + substituteInPlace "platform_linux/upload_fw_dfu.sh" \ + --replace-fail "/bin/dfu-util" "" + substituteInPlace "platform_linux/upload_fw_dfu.sh" \ + --replace-fail "./dfu-util" "${dfu-util-ksoloti}/bin/dfu-util" + + */ + patchPhase = '' + # Hardcode path to "make" + for f in "firmware/compile_firmware_linux.sh" \ + "firmware/compile_patch_linux.sh"; do + substituteInPlace "$f" \ + --replace "make" "${gnumake}/bin/make" + done + + # Fix build version + substituteInPlace "build.xml" \ + --replace-fail "(git missing)" "${version}" + # Remove build time + substituteInPlace "build.xml" \ + --replace-fail "" "" + substituteInPlace "build.xml" \ + --replace-fail \ + '' \ + '' + substituteInPlace "build.xml" \ + --replace-fail "" "" + substituteInPlace "build.xml" \ + --replace-fail \ + '{line.separator}' \ + '{line.separator} ' + ''; + + buildPhase = '' + ( + find . -exec touch -d '1970-01-01 00:00' {} \; + (cd platform_linux; PATH="${pkgs.gcc-arm-embedded}/bin:$PATH" sh compile_firmware.sh BOARD_AXOLOTI_CORE) + faketime "1970-01-01 00:00:00" ant -Dbuild.runtime=true + ) + ''; + + installPhase = '' + mkdir -p $out/bin $out/share/ksoloti + + cp -r doc firmware chibios platform_linux CMSIS *.txt $out/share/ksoloti/ + install -vD dist/Ksoloti.jar $out/share/ksoloti/ + + patchShebangs $out/share/ksoloti/platform_linux/*.sh + + rm $out/share/ksoloti/platform_linux/bin/dfu-util + ln -s ${dfu-util}/bin/dfu-util $out/share/ksoloti/platform_linux/bin/dfu-util + + makeWrapper ${jdk}/bin/java $out/bin/ksoloti --add-flags "-Dksoloti_release=$out/share/ksoloti -Dksoloti_runtime=$out/share/ksoloti -jar $out/share/ksoloti/Ksoloti.jar" --prefix PATH : ${ + lib.makeBinPath [ + pkgs.gcc-arm-embedded + ] + } + ''; + + meta = with lib; { + homepage = "http://ksoloti.github.io"; + description = '' + Sketching embedded digital audio algorithms. + + To fix permissions of the Ksoloti USB device node, add a similar udev rule to services.udev.extraRules: + SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="0442", OWNER="someuser", GROUP="somegroup" + ''; + license = licenses.gpl3; + maintainers = with maintainers; [ ]; + }; +} diff --git a/modules/udmx.nix b/pkgs/ksoloti/module.nix similarity index 58% rename from modules/udmx.nix rename to pkgs/ksoloti/module.nix index da8df93..2948be5 100644 --- a/modules/udmx.nix +++ b/pkgs/ksoloti/module.nix @@ -1,8 +1,5 @@ -{ config, lib, pkgs, ... }: - { services.udev.extraRules = '' - # uDMX - SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", GROUP="users", MODE="0660" + SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="0442", OWNER="jalr", GROUP="users" ''; } diff --git a/pkgs/modules.nix b/pkgs/modules.nix index 02b906b..195958b 100644 --- a/pkgs/modules.nix +++ b/pkgs/modules.nix @@ -1,7 +1,6 @@ -{ pkgs, ... }: - { imports = [ - ./pretix/module.nix + ./ksoloti/module.nix + ./myintercom-doorbell/module.nix ]; } diff --git a/pkgs/myintercom-doorbell/.envrc b/pkgs/myintercom-doorbell/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/pkgs/myintercom-doorbell/.envrc @@ -0,0 +1 @@ +use nix diff --git a/pkgs/myintercom-doorbell/.gitignore b/pkgs/myintercom-doorbell/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/pkgs/myintercom-doorbell/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/pkgs/myintercom-doorbell/README.md b/pkgs/myintercom-doorbell/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/myintercom-doorbell/default.nix b/pkgs/myintercom-doorbell/default.nix new file mode 100644 index 0000000..37b3711 --- /dev/null +++ b/pkgs/myintercom-doorbell/default.nix @@ -0,0 +1,7 @@ +{ poetry2nix }: + +poetry2nix.mkPoetryApplication { + pname = "myintercom-audiosocket"; + version = "0.0.1"; + projectDir = ./.; +} diff --git a/pkgs/myintercom-doorbell/module.nix b/pkgs/myintercom-doorbell/module.nix new file mode 100644 index 0000000..cddb862 --- /dev/null +++ b/pkgs/myintercom-doorbell/module.nix @@ -0,0 +1,157 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.myintercom-doorbell; + mediamtxConfig = pkgs.writeTextFile { + name = "myintercom-doorbell-cam-proxy-config"; + text = lib.generators.toJSON { } { + paths.sprechanlage = { + source = "rtsp://${cfg.username}:__PASSWORD__@${cfg.host}/axis-media/media.amp?videocodec=h264&resolution=1280x720&fps=8&audio=0"; + rtspTransport = "tcp"; + }; + protocols = [ "tcp" ]; + hls = false; + rtmp = false; + rtsp = false; + srt = false; + webrtc = true; + webrtcAdditionalHosts = [ cfg.cam.bindAddress ]; + webrtcLocalTCPAddress = "${cfg.cam.bindAddress}:${toString cfg.cam.webrtcIceTcpPort}"; + }; + }; +in +{ + options.services.myintercom-doorbell = with lib; with lib.types; { + enable = mkEnableOption "Enable myintercom service"; + cam = { + enable = mkEnableOption "Enable cam proxy service"; + bindAddress = mkOption { + type = types.str; + description = "The Address the service binds to."; + example = "10.0.0.1"; + }; + webrtcPort = mkOption { + type = types.port; + description = "Port the WebRTC service binds to."; + default = 8889; + }; + webrtcIceTcpPort = mkOption { + type = types.port; + description = "Port (udp) the WebRTC ICE service binds to."; + default = 8189; + }; + }; + host = mkOption { + type = types.str; + description = "The Hostname of myintercom."; + example = "myintercom.lan.example.net"; + }; + username = mkOption { + type = types.str; + description = "Username for basic auth."; + }; + passwordFile = mkOption { + type = types.path; + description = "Path to the file that contains the basic auth password."; + }; + audiosocket = { + address = mkOption { + type = types.str; + description = "Address the AudioSocket binds to."; + default = "127.0.0.1"; + }; + port = mkOption { + type = types.port; + description = "Port the AudioSocket binds to."; + default = 9092; + }; + uuid = mkOption { + type = types.str; + example = "e461837f-22b0-4652-955f-e1a444f3a42e"; + }; + }; + callerId = mkOption { + type = types.str; + description = "The display name to show when the doorbell rings a phone."; + example = "Doorbell"; + }; + dialTime = mkOption { + type = types.int; + description = "The duration how long to wait for the call to be answered."; + default = 45; + }; + }; + + config = lib.mkIf cfg.enable { + environment.etc."myintercom-doorbell/settings.json".text = builtins.toJSON { + inherit (cfg) host; + inherit (cfg) username; + inherit (cfg) passwordFile; + audiosocket = { + inherit (cfg.audiosocket) address; + inherit (cfg.audiosocket) port; + inherit (cfg.audiosocket) uuid; + }; + inherit (cfg) callerId; + inherit (cfg) dialTime; + }; + + systemd.services = { + myintercom-doorbell-poll = { + inherit (cfg) enable; + description = "Polls myintercom doorbell ring button status."; + after = [ "asterisk.service" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = "asterisk"; + # @TODO: Use unstable only until 23.11 is released + ExecStart = "${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-poll"; + }; + }; + myintercom-doorbell-audiosocket = { + inherit (cfg) enable; + description = "myintercom doorbell AudioSocket for Asterisk"; + requires = [ "asterisk.service" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + CapabilityBoundingSet = null; + PrivateUsers = true; + ProtectHome = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallFilter = "@system-service"; + LoadCredential = "password:${cfg.passwordFile}"; + Environment = [ + "LISTEN_ADDRESS=${cfg.audiosocket.address}" + "LISTEN_PORT=${toString cfg.audiosocket.port}" + "HOST=${cfg.host}" + "USERNAME=${cfg.username}" + "PASSWORD_FILE=%d/password" + ]; + ExecStart = "${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-audiosocket"; + }; + }; + myintercom-doorbell-cam-proxy = { + inherit (cfg.cam) enable; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "Proxies the videostream of myintercom doorbell."; + script = '' + sed "s:__PASSWORD__:$(cat "$PASSWORD_FILE"):" "${mediamtxConfig}" | ${pkgs.mediamtx}/bin/mediamtx /dev/stdin + ''; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + LoadCredential = "password:${cfg.passwordFile}"; + Environment = [ + "PASSWORD_FILE=%d/password" + ]; + }; + }; + }; + }; +} diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py b/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py new file mode 100644 index 0000000..0bb4453 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py @@ -0,0 +1,83 @@ +import socket +from threading import Thread +from dataclasses import dataclass + +from .connection import Connection + + +@dataclass +class audioop_struct: + ratecv_state: None + rate: int + channels: int + ulaw2lin: bool + lin2ulaw: bool + + +# Make a single, global object instance, +# then loop with listen() method alone where needed + + +# Creates a new audiosocket object +class Audiosocket: + def __init__(self, bind_info, timeout=None): + # By default, features of audioop (for example: resampling + # or re-mixng input/output) are disabled + self.user_resample = None + self.asterisk_resample = None + + if not isinstance(bind_info, tuple): + raise TypeError("Expected tuple (addr, port), received", type(bind_info)) + + self.addr, self.port = bind_info + + self.initial_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.initial_sock.bind((self.addr, self.port)) + self.initial_sock.settimeout(timeout) + self.initial_sock.listen(1) + + # If the user didn't specify a port, the one that the operating system + # chose is availble in this attribute + self.port = self.initial_sock.getsockname()[1] + + # Optionally prepares audio sent by the user to + # the specifications needed by audiosocket (16-bit, 8KHz mono LE PCM). + # Audio sent in must be in PCM or ULAW format + def prepare_input(self, inrate=44000, channels=2, ulaw2lin=False, lin2ulaw=False): + self.user_resample = audioop_struct( + rate=inrate, + channels=channels, + ulaw2lin=ulaw2lin, + lin2ulaw=lin2ulaw, + ratecv_state=None, + ) + + # Optionally prepares audio sent by audiosocket to + # the specifications of the user + def prepare_output(self, outrate=44000, channels=2, ulaw2lin=False, lin2ulaw=False): + self.asterisk_resample = audioop_struct( + rate=outrate, + channels=channels, + ulaw2lin=ulaw2lin, + lin2ulaw=lin2ulaw, + ratecv_state=None, + ) + + def listen(self): + conn, peer_addr = self.initial_sock.accept() + connection = Connection( + conn, + peer_addr, + self.user_resample, + self.asterisk_resample, + ) + + connection_thread = Thread(target=connection._process, args=()) + connection_thread.start() + + return connection + + # If we want this single object to serve multiple simultaneous + # connections, accept() will have to be put in a while loop + # If this does become the case, what is the best way to deliver the + # queue objects to the caller, keep them wrapped in read/write methods? diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py b/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py new file mode 100644 index 0000000..a5e968e --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py @@ -0,0 +1,251 @@ +# Standard Python modules +import audioop +from queue import Queue, Empty +from dataclasses import dataclass +from threading import Lock +from time import sleep + + +# A sort of imitation struct that holds all of the possible +# AudioSocket message types + + +@dataclass(frozen=True) +class types_struct: + uuid: bytes = b"\x01" # Message payload contains UUID set in Asterisk Dialplan + audio: bytes = ( + b"\x10" # * Message payload contains 8Khz 16-bit mono LE PCM audio (* See Github readme) + ) + silence: bytes = ( + b"\x02" # Message payload contains silence (I've never seen this occur personally) + ) + hangup: bytes = ( + b"\x00" # Tell Asterisk to hangup the call (This doesn't appear to ever be sent from Asterisk to us) + ) + error: bytes = b"\xff" # Message payload contains an error from Asterisk + + +types = types_struct() + + +# The size of 20ms of 8KHz 16-bit mono LE PCM represented as a +# 16 bit (2 byte, size of length header) unsigned BE integer + +# This amount of the audio data mentioned above is equal +# to 320 bytes, which is the required payload size when +# sending audio back to AudioSocket for playback on the +# bridged channel. Sending more or less data will result in distorted sound +PCM_SIZE = (320).to_bytes(2, "big") + + +# Similar to one above, this holds all the possible +# AudioSocket related error codes Asterisk can send us + + +@dataclass(frozen=True) +class errors_struct: + none: bytes = b"\x00" + hangup: bytes = b"\x01" + frame: bytes = b"\x02" + memory: bytes = b"\x04" + + +errors = errors_struct() + + +class Connection: + def __init__(self, conn, peer_addr, user_resample, asterisk_resample): + self.conn = conn + self.peer_addr = peer_addr + self.uuid = None + self.connected = True # An instance gets created because a connection occurred + self._user_resample = user_resample + self._asterisk_resample = asterisk_resample + + # Underlying Queue objects for passing incoming/outgoing audio between threads + self._rx_q = Queue(500) + self._tx_q = Queue(500) + + self._lock = Lock() + + # Splits data sent by AudioSocket into three different peices + def _split_data(self, data): + if len(data) < 3: + print( + "[AUDIOSOCKET WARNING] The data received was less than 3 bytes, " + + "the minimum length data from Asterisk AudioSocket should be." + ) + + return b"\x00", 0, bytes(320) + + else: + # type length payload + return data[:1], int.from_bytes(data[1:3], "big"), data[3:] + + # If the type of message received was an error, this + # prints an explanation of the specific one that occurred + def _decode_error(self, payload): + if payload == errors.none: + print("[ASTERISK ERROR] No error code present") + + elif payload == errors.hangup: + print("[ASTERISK ERROR] The called party hungup") + + elif payload == errors.frame: + print("[ASTERISK ERROR] Failed to forward frame") + + elif payload == errors.memory: + print("[ASTERISK ERROR] Memory allocation error") + + return + + # Gets AudioSocket audio from the rx queue + def read(self): + try: + audio = self._rx_q.get(timeout=0.2) + + # If for some reason we receive less than 320 bytes + # of audio, add silence (padding) to the end. This prevents + # audioop related errors that are caused by the current frame + # not being the same size as the last + if len(audio) != 320: + audio += bytes(320 - len(audio)) + + except Empty: + return bytes(320) + + if self._asterisk_resample: + # If AudioSocket is bridged with a channel + # using the ULAW audio codec, the user can specify + # to have it converted to linear encoding upon reading. + if self._asterisk_resample.ulaw2lin: + audio = audioop.ulaw2lin(audio, 2) + + if self._asterisk_resample.lin2ulaw: + audio = audioop.lin2ulaw(audio, 2) + + # If the user requested an outrate different + # from the default, then resample it to the rate they specified + if self._asterisk_resample.rate != 8000: + audio, self._asterisk_resample.ratecv_state = audioop.ratecv( + audio, + 2, + 1, + 8000, + self._asterisk_resample.rate, + self._asterisk_resample.ratecv_state, + ) + + # If the user requested the output be in stereo, + # then convert it from mono + if self._asterisk_resample.channels == 2: + audio = audioop.tostereo(audio, 2, 1, 1) + + return audio + + # Puts user supplied audio into the tx queue + def write(self, audio): + if self._user_resample: + # The user can also specify to have ULAW encoded source audio + # converted to linear encoding upon being written. + if self._user_resample.ulaw2lin: + # Possibly skip downsampling if this was triggered, as + # while ULAW encoded audio can be sampled at rates other + # than 8KHz, since this is telephony related, it's unlikely. + audio = audioop.ulaw2lin(audio, 2) + if self._user_resample.lin2ulaw: + audio = audioop.lin2ulaw(audio, 2) + + # If the audio isn't already sampled at 8KHz, + # then it needs to be downsampled first + if self._user_resample.rate != 8000: + audio, self._user_resample.ratecv_state = audioop.ratecv( + audio, + 2, + self._user_resample.channels, + self._user_resample.rate, + 8000, + self._user_resample.ratecv_state, + ) + + # If the audio isn't already in mono, then + # it needs to be downmixed as well + if self._user_resample.channels == 2: + audio = audioop.tomono(audio, 2, 1, 1) + + self._tx_q.put(audio) + + # *** This may interfere with the thread executing _process, consider + # sending type through queue as well, so a hangup message can be done properly + + # Tells Asterisk to hangup the call from it's end. + # Although after the call is hungup, the socket on Asterisk's end + # closes the connection via an abrupt RST packet, resulting in a "Connection reset by peer" + # error on our end. Unfortunately, using try and except around self.conn.recv() is as + # clean as I think it can be right now + def hangup(self): + # Three bytes of 0 indicate a hangup message + with self._lock: + self.conn.send(types.hangup * 3) + + sleep(0.2) + return + + def _process(self): + # The main audio receiving/sending loop, this loops + # until AudioSocket stops sending us data, the hangup() method is called or an error occurs. + # A disconnection can be triggered from the users end by calling the hangup() method + while True: + data = None + + try: + with self._lock: + data = self.conn.recv(323) + + except ConnectionResetError: + pass + + if not data: + self.connected = False + self.conn.close() + return + + type, length, payload = self._split_data(data) + + if type == types.audio: + # Adds received audio into the rx queue + if self._rx_q.full(): + print( + "[AUDIOSOCKET WARNING] The inbound audio queue is full! This most " + + "likely occurred because the read() method is not being called, skipping frame" + ) + + else: + self._rx_q.put(payload) + + # To prevent the tx queue from blocking all execution if + # the user doesn't supply it with (enough) audio, silence is + # generated manually and sent back to AudioSocket whenever its empty. + if self._tx_q.empty(): + self.conn.send(types.audio + PCM_SIZE + bytes(320)) + + else: + # If a single peice of audio data in the rx queue is larger than + # 320 bytes, slice it before sending, however... + # If the audio data to send is larger than this, then + # it's probably in the wrong format to begin with and wont be + # played back properly even when sliced. + audio_data = self._tx_q.get()[:320] + + with self._lock: + self.conn.send( + types.audio + + len(audio_data).to_bytes(2, "big") + + audio_data + ) + + elif type == types.error: + self._decode_error(payload) + + elif type == types.uuid: + self.uuid = payload.hex() diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py b/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py new file mode 100644 index 0000000..574dc28 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py @@ -0,0 +1,101 @@ +import os +import sys +import urllib3 + +from multiprocessing import Pipe, Process +from threading import Thread + +from .audiosocket import Audiosocket + + +def open_url(direction, connection, host, username, password): + print(f"start {direction}", flush=True) + if direction not in ("transmit", "receive"): + raise NotImplementedError + method, headers = { + "receive": ("GET", {}), + "transmit": ("POST", {"Content-Type": "audio/basic", "Content-Length": "0"}), + }[direction] + + url = f"http://{host}/axis-cgi/audio/{direction}.cgi" + + http = urllib3.PoolManager() + + print(f"start {direction} request", flush=True) + + response = http.request( + method, + url, + headers={ + **urllib3.make_headers(basic_auth=f"{username}:{password}"), + **headers, + }, + preload_content=False, + body=( + None + if direction != "transmit" + else os.fdopen(connection.fileno(), "rb", buffering=0) + ), + ) + print(f"{direction} status is {response.status}", flush=True) + + if direction == "receive": + for data in response.stream(amt=160): + connection.send_bytes(data) + + +def handle_connection(call, host, username, password): + print(f"Received connection from {call.peer_addr}") + + pipe_transmit_in, pipe_transmit_out = Pipe() + doorbell_transmit_process = Process( + target=open_url, args=("transmit", pipe_transmit_out, host, username, password) + ) + doorbell_transmit_process.start() + + pipe_receive_in, pipe_receive_out = Pipe() + doorbell_receive_process = Process( + target=open_url, args=("receive", pipe_receive_in, host, username, password) + ) + doorbell_receive_process.start() + + with os.fdopen(os.dup(pipe_transmit_in.fileno()), "wb", buffering=0) as f_transmit: + while call.connected: + f_transmit.write(call.read()) + call.write(pipe_receive_out.recv_bytes()) + + print(f"Connection with {call.peer_addr} is now over") + doorbell_transmit_process.terminate() + doorbell_receive_process.terminate() + doorbell_transmit_process.join() + doorbell_receive_process.join() + + +def main(): + audiosocket = Audiosocket( + (os.environ["LISTEN_ADDRESS"], int(os.environ["LISTEN_PORT"])) + ) + + audiosocket.prepare_output(outrate=8000, channels=1, lin2ulaw=True) + audiosocket.prepare_input(inrate=8000, channels=1, ulaw2lin=True) + + print("Listening for new connections " f"from Asterisk on port {audiosocket.port}") + host = os.environ["HOST"] + username = os.environ["USERNAME"] + + with open(os.environ["PASSWORD_FILE"], "r", encoding="utf-8") as f: + password = f.read() + + while True: + call = audiosocket.listen() + + call_thread = Thread( + target=handle_connection, args=(call, host, username, password) + ) + call_thread.start() + + call_thread.join() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/open.py b/pkgs/myintercom-doorbell/myintercom_doorbell/open.py new file mode 100644 index 0000000..f3a7211 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/open.py @@ -0,0 +1,36 @@ +import json +import os +import sys +import tempfile + +import urllib3 + + +def open_door(host, username, password): + urllib3.PoolManager().request( + "GET", + f"http://{host}/local/Doorcom/door.cgi?r=1", + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + ) + + +def read_file_contents(file_name): + with open(file_name, "r", encoding="utf-8") as f: + return f.read() + + +def main(): + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + open_door( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/service.py b/pkgs/myintercom-doorbell/myintercom_doorbell/service.py new file mode 100644 index 0000000..3def826 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/service.py @@ -0,0 +1,107 @@ +import json +import os +import tempfile + +import urllib3 + +import time + + +def send_open_door_request(host, username, password): + urllib3.PoolManager( + timeout=urllib3.Timeout( + connect=3.0, + read=300, + ) + ).request( + "GET", + f"http://{host}/local/Doorcom/door.cgi?r=1", + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + ) + + +def get_ring_status(host, username, password): + url = f"http://{host}/local/Doorcom/monitor.cgi?ring=1" + print(f"sending request to {url}", flush=True) + try: + response = urllib3.PoolManager().request( + "GET", + url, + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + preload_content=False, + decode_content=True, + ) + except urllib3.exceptions.MaxRetryError: + return + + while True: + line = response.readline() + if line != b"--ioboundary\r\n": + continue + header = response.readline() + if header != b"Content-Type: text/plain\r\n": + continue + if response.readline() != b"\r\n": + continue + data = [] + while True: + line = response.readline() + if line != b"\r\n": + data.append(line.decode().rstrip()) + else: + if data: + print(f"received: {data}", flush=True) + yield data + break + + +def read_file_contents(file_name): + with open(file_name, "r", encoding="utf-8") as f: + return f.read() + + +def open_door(): + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + send_open_door_request( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ) + + +def poll(): + outgoing_dir = "/var/spool/asterisk/outgoing/" + + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + audiosocket = f"{config['audiosocket']['address']}:{config['audiosocket']['port']}/{config['audiosocket']['uuid']}" + callfile_content = ( + f"Channel: Audiosocket/{audiosocket}\n" + "Context: doorbell\n" + f"CallerID: {config['callerId']}\n" + "Extension: s\n" + "Priority: 1\n" + ) + + while True: + for status in get_ring_status( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ): + if status == ["1:H"]: + print("ringing", flush=True) + with tempfile.NamedTemporaryFile(dir="/var/tmp", mode="w") as f: + f.write(callfile_content) + os.chmod(f.name, 0o644) + os.link( + f.name, os.path.join(outgoing_dir, os.path.basename(f.name)) + ) + time.sleep(config["dialTime"]) diff --git a/pkgs/myintercom-doorbell/poetry.lock b/pkgs/myintercom-doorbell/poetry.lock new file mode 100644 index 0000000..0ce0d9b --- /dev/null +++ b/pkgs/myintercom-doorbell/poetry.lock @@ -0,0 +1,24 @@ +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.1" +python-versions = "^3.12" +content-hash = "a2502d4bca34c8c9ddc7579666a62dc15d3573a0240075cb566922a1d031831e" diff --git a/pkgs/myintercom-doorbell/pyproject.toml b/pkgs/myintercom-doorbell/pyproject.toml new file mode 100644 index 0000000..3a57dc6 --- /dev/null +++ b/pkgs/myintercom-doorbell/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "myintercom-doorbell" +version = "0.1.0" +description = "" +authors = ["Jakob Lechner "] +readme = "README.md" +packages = [{include = "myintercom_doorbell"}] + +[tool.poetry.dependencies] +python = "^3.12" +urllib3 = "^2.5.0" + +[tool.poetry.scripts] +myintercom-doorbell-audiosocket = "myintercom_doorbell.myintercom_audiosocket:main" +myintercom-doorbell-open-door = "myintercom_doorbell.service:open_door" +myintercom-doorbell-poll = "myintercom_doorbell.service:poll" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pkgs/pretix/shell.nix b/pkgs/myintercom-doorbell/shell.nix similarity index 100% rename from pkgs/pretix/shell.nix rename to pkgs/myintercom-doorbell/shell.nix diff --git a/pkgs/pomodoro-timer/default.nix b/pkgs/pomodoro-timer/default.nix new file mode 100644 index 0000000..a3638f8 --- /dev/null +++ b/pkgs/pomodoro-timer/default.nix @@ -0,0 +1,61 @@ +{ lib +, stdenv +, yad +, uair +, writeShellScript +, makeDesktopItem +, imagemagick +}: + +let + pomodoroTimer = writeShellScript "pomodoro-timer" '' + export PATH=${lib.makeBinPath [yad uair]} + uairctl listen -o yad \ + | yad \ + --title="Pomodoro" \ + --geometry="300x50" \ + --scale \ + --progress \ + --no-buttons \ + --css="* { font-size: 60px;} progress { min-height: 1200px; margin: -100px -8px -6px;}" + ''; +in +stdenv.mkDerivation rec { + pname = "pomodoro-timer"; + version = "1.0.0"; + src = ./pomodorotimer.svg; + dontUnpack = true; + + installPhase = '' + icon_size=64x64 + dir=$out/share/icons/hicolor/scalable/apps/ + mkdir -p \ + $out/share/icons/hicolor/scalable/apps/ \ + $out/share/icons/hicolor/$icon_size/apps/ + + cp $src $dir/pomodorotimer.svg + mkdir $out/bin + cp "${pomodoroTimer}" $out/bin/pomodoro-timer + ln -s "${desktopItem}/share/applications" $out/share/ + + ${imagemagick}/bin/convert \ + -size $icon_size \ + $src \ + $out/share/icons/hicolor/$icon_size/apps/pomodorotimer.png + ''; + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = "pomodorotimer"; + desktopName = pname; + comment = meta.description; + categories = [ "Utility" ]; + }; + + meta = with lib; { + description = "Pomodoro timer (GUI for uair)"; + maintainers = with maintainers; [ jalr ]; + platforms = platforms.linux; + }; +} diff --git a/pkgs/pomodoro-timer/pomodorotimer.svg b/pkgs/pomodoro-timer/pomodorotimer.svg new file mode 100644 index 0000000..b771a33 --- /dev/null +++ b/pkgs/pomodoro-timer/pomodorotimer.svg @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + Pomodoro Timer + + + + + + + image/svg+xml + + + + + Openclipart + + + Pomodoro Timer + 2011-05-03T21:58:11 + The Pomodoro Technique® is a way to get the most out of time management. Develop by Francesco Cirillo. + https://openclipart.org/detail/135631/pomodoro-timer-by-fairhonanth + + + fairhonanth + + + + + management + pomodoro + productivity + techinique + timer + + + + + + + + + + + diff --git a/pkgs/pretix/module.nix b/pkgs/pretix/module.nix deleted file mode 100644 index 12872bb..0000000 --- a/pkgs/pretix/module.nix +++ /dev/null @@ -1,267 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.services.pretix; - name = "pretix"; - user = "pretix"; - group = "pretix"; - bind = { - host = "127.0.0.1"; - port = 8000; - }; - postgresql = { - database = "pretix"; - user = "pretix"; - password = "pretix"; - }; - redisPort = 6379; - urlScheme = if cfg.enableTls then "https" else "http"; - url = "${urlScheme}://${cfg.domain}"; - toBool = x: if x then "on" else "off"; - hstsHeader = if cfg.enableTls then "add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" always;" else ""; - pythonPackages = pkgs.pretix.passthru.pythonModule.passthru.pkgs; - python = pkgs.pretix.passthru.python; - runCommandArgs = { - # Sets PYTHONPATH in derivation - buildInputs = [ - pkgs.pretix - pythonPackages.gunicorn - pythonPackages.celery - ]; - }; - staticRoot = pkgs.pretix-static; - environmentFile = pkgs.runCommand "pretix-environ" runCommandArgs '' - cat > $out <=4.0)"] - -[[package]] -name = "asgiref" -version = "3.7.2" -description = "ASGI specs, helper code, and adapters" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} - -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] - -[[package]] -name = "async-timeout" -version = "4.0.2" -description = "Timeout context manager for asyncio programs" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - -[[package]] -name = "babel" -version = "2.12.1" -description = "Internationalization utilities" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.2" -description = "Screen-scraping library" -category = "main" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "billiard" -version = "4.1.0" -description = "Python multiprocessing fork with improvements and bugfixes" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "billiard-4.1.0-py3-none-any.whl", hash = "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a"}, - {file = "billiard-4.1.0.tar.gz", hash = "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5"}, -] - -[[package]] -name = "bleach" -version = "5.0.1" -description = "An easy safelist-based HTML-sanitizing tool." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, - {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, -] - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.2)"] -dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] - -[[package]] -name = "cbor2" -version = "5.4.6" -description = "CBOR (de)serializer with extensive tag support" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cbor2-5.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:309fffbb7f561d67f02095d4b9657b73c9220558701c997e9bfcfbca2696e927"}, - {file = "cbor2-5.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff95b33e5482313a74648ca3620c9328e9f30ecfa034df040b828e476597d352"}, - {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9eb582fce972f0fa429d8159b7891ff8deccb7affc4995090afc61ce0d328a"}, - {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3950be57a1698086cf26d8710b4e5a637b65133c5b1f9eec23967d4089d8cfed"}, - {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78304df140b9e13b93bcbb2aecee64c9aaa9f1cadbd45f043b5e7b93cc2f21a2"}, - {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e73ca40dd3c7210ff776acff9869ddc9ff67bae7c425b58e5715dcf55275163f"}, - {file = "cbor2-5.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:0b956f19e93ba3180c336282cd1b6665631f2d3a196a9c19b29a833bf979e7a4"}, - {file = "cbor2-5.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c12c0ab78f5bc290b08a79152a8621822415836a86f8f4b50dadba371736fda"}, - {file = "cbor2-5.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3545b16f9f0d5f34d4c99052829c3726020a07be34c99c250d0df87418f02954"}, - {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24144822f8d2b0156f4cda9427f071f969c18683ffed39663dc86bc0a75ae4dd"}, - {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1835536e76ea16e88c934aac5e369ba9f93d495b01e5fa2d93f0b4986b89146d"}, - {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:39452c799453f5bf33281ffc0752c620b8bfa0b7c13070b87d370257a1311976"}, - {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3316f09a77af85e7772ecfdd693b0f450678a60b1aee641bac319289757e3fa0"}, - {file = "cbor2-5.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:456cdff668a50a52fdb8aa6d0742511e43ed46d6a5b463dba80a5a720fa0d320"}, - {file = "cbor2-5.4.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9394ca49ecdf0957924e45d09a4026482d184a465a047f60c4044eb464c43de9"}, - {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56dfa030cd3d67e5b6701d3067923f2f61536a8ffb1b45be14775d1e866b59ae"}, - {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5094562dfe3e5583202b93ef7ca5082c2ba5571accb2c4412d27b7d0ba8a563"}, - {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:94f844d0e232aca061a86dd6ff191e47ba0389ddd34acb784ad9a41594dc99a4"}, - {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7bbd3470eb685325398023e335be896b74f61b014896604ed45049a7b7b6d8ac"}, - {file = "cbor2-5.4.6-cp37-cp37m-win_amd64.whl", hash = "sha256:0bd12c54a48949d11f5ffc2fa27f5df1b4754111f5207453e5fae3512ebb3cab"}, - {file = "cbor2-5.4.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2984a488f350aee1d54fa9cb8c6a3c1f1f5b268abbc91161e47185de4d829f3"}, - {file = "cbor2-5.4.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c285a2cb2c04004bfead93df89d92a0cef1874ad337d0cb5ea53c2c31e97bfdb"}, - {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6709d97695205cd08255363b54afa035306d5302b7b5e38308c8ff5a47e60f2a"}, - {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96087fa5336ebfc94465c0768cd5de0fcf9af3840d2cf0ce32f5767855f1a293"}, - {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0d2b926b024d3a1549b819bc82fdc387062bbd977b0299dd5fa5e0ea3267b98b"}, - {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6e1b5aee920b6a2f737aa12e2b54de3826b09f885a7ce402db84216343368140"}, - {file = "cbor2-5.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:79e048e623846d60d735bb350263e8fdd36cb6195d7f1a2b57eacd573d9c0b33"}, - {file = "cbor2-5.4.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80ac8ba450c7a41c5afe5f7e503d3092442ed75393e1de162b0bf0d97edf7c7f"}, - {file = "cbor2-5.4.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ce1a2c272ba8523a55ea2f1d66e3464e89fa0e37c9a3d786a919fe64e68dbd7"}, - {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1618d16e310f7ffed141762b0ff5d8bb6b53ad449406115cc465bf04213cefcf"}, - {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bbbdb2e3ef274865dc3f279aae109b5d94f4654aea3c72c479fb37e4a1e7ed7"}, - {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f9c702bee2954fffdfa3de95a5af1a6b1c5f155e39490353d5654d83bb05bb9"}, - {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b9f3924da0e460a93b3674c7e71020dd6c9e9f17400a34e52a88c0af2dcd2aa"}, - {file = "cbor2-5.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:d54bd840b4fe34f097b8665fc0692c7dd175349e53976be6c5de4433b970daa4"}, - {file = "cbor2-5.4.6-py3-none-any.whl", hash = "sha256:181ac494091d1f9c5bb373cd85514ce1eb967a8cf3ec298e8dfa8878aa823956"}, - {file = "cbor2-5.4.6.tar.gz", hash = "sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7"}, -] - -[package.extras] -doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "celery" -version = "5.3.1" -description = "Distributed Task Queue." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "celery-5.3.1-py3-none-any.whl", hash = "sha256:27f8f3f3b58de6e0ab4f174791383bbd7445aff0471a43e99cfd77727940753f"}, - {file = "celery-5.3.1.tar.gz", hash = "sha256:f84d1c21a1520c116c2b7d26593926581191435a03aa74b77c941b93ca1c6210"}, -] - -[package.dependencies] -billiard = ">=4.1.0,<5.0" -click = ">=8.1.2,<9.0" -click-didyoumean = ">=0.3.0" -click-plugins = ">=1.1.1" -click-repl = ">=0.2.0" -kombu = ">=5.3.1,<6.0" -python-dateutil = ">=2.8.2" -tzdata = ">=2022.7" -vine = ">=5.0.0,<6.0" - -[package.extras] -arangodb = ["pyArango (>=2.0.1)"] -auth = ["cryptography (==41.0.1)"] -azureblockblob = ["azure-storage-blob (>=12.15.0)"] -brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] -cassandra = ["cassandra-driver (>=3.25.0,<4)"] -consul = ["python-consul2 (==0.1.5)"] -cosmosdbsql = ["pydocumentdb (==2.3.5)"] -couchbase = ["couchbase (>=3.0.0)"] -couchdb = ["pycouchdb (==1.14.2)"] -django = ["Django (>=2.2.28)"] -dynamodb = ["boto3 (>=1.26.143)"] -elasticsearch = ["elasticsearch (<8.0)"] -eventlet = ["eventlet (>=0.32.0)"] -gevent = ["gevent (>=1.5.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -memcache = ["pylibmc (==1.6.3)"] -mongodb = ["pymongo[srv] (>=4.0.2)"] -msgpack = ["msgpack (==1.0.5)"] -pymemcache = ["python-memcached (==1.59)"] -pyro = ["pyro4 (==4.82)"] -pytest = ["pytest-celery (==0.0.0)"] -redis = ["redis (>=4.5.2,!=4.5.5)"] -s3 = ["boto3 (>=1.26.143)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -solar = ["ephem (==4.1.4)"] -sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] -sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] -tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] -zstd = ["zstandard (==0.21.0)"] - -[[package]] -name = "certifi" -version = "2023.5.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "chardet" -version = "5.1.0" -description = "Universal encoding detector for Python 3" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, - {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "click" -version = "8.1.4" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.4-py3-none-any.whl", hash = "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3"}, - {file = "click-8.1.4.tar.gz", hash = "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "click-didyoumean" -version = "0.3.0" -description = "Enables git-like *did-you-mean* feature in click" -category = "main" -optional = false -python-versions = ">=3.6.2,<4.0.0" -files = [ - {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, - {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, -] - -[package.dependencies] -click = ">=7" - -[[package]] -name = "click-plugins" -version = "1.1.1" -description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, - {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, -] - -[package.dependencies] -click = ">=4.0" - -[package.extras] -dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] - -[[package]] -name = "click-repl" -version = "0.3.0" -description = "REPL plugin for Click" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, - {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, -] - -[package.dependencies] -click = ">=7.0" -prompt-toolkit = ">=3.0.36" - -[package.extras] -testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cryptography" -version = "41.0.1" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"}, - {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"}, - {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"}, - {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"}, - {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"}, - {file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"}, - {file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039"}, - {file = "cryptography-41.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"}, - {file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"}, - {file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"}, -] - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - -[[package]] -name = "css-inline" -version = "0.8.7" -description = "Fast CSS inlining written in Rust" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "css_inline-0.8.7-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:3604d7af3df90681a5a31d2a1438bd237ae1ba171f2ea1cb62824f4909238a63"}, - {file = "css_inline-0.8.7-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6e0afb35e17888b2ddd8efee738f1f68ae569615cb32b66427381372cab2d6b2"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:84e2b2c5c7c16b5ff546f9fae53e7f0d24bc63e9de6a2a655809c3698ad9f9e2"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773a150ba085d73ea8a4f27f562dcf90a7bc8dcecf0d1867b660f22d036c6a6a"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:39a90bd53272ee68a4d7909dc9b03240b296c50249b964cb253faf361c90e9dd"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ee0894e7a72434750799fced0c7404fed19f3d0538c7fb3ff61d4efacd473d"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5b08837acd1fe60a8f8cbd44dfce88dda1676aca47eeb51bf512c02e90803b77"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6c0f02ddc5b694520d0fb8db7961e703120a373e516a74cfa6c8303b1c131e42"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:51cc996cfce5fd10aa5e41569c664cded198f30f4706e699e97942893aa9e7f9"}, - {file = "css_inline-0.8.7-cp37-abi3-win32.whl", hash = "sha256:c5910202e7583f0d1b6aea34d63cd378e28808f8bcf210a9c961cb26ccc5ed25"}, - {file = "css_inline-0.8.7-cp37-abi3-win_amd64.whl", hash = "sha256:6803c2fb2064330de1761a44ae86ab6c9b78d1761c36ebdd8919346e595d702d"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:36a392ed87f840621838e63937e151d236a62cfdba2760f503273330691ea3d8"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0757b71ae23d4131d7ca274bb35d41f5faa41b88eed9090df3ab409689d055d"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c9ea5e288e644bd78dbfd3d2bae9c33eb11e3f02d2c61cf4b5683c8f97da"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:819bf4c331e59da07d824529027c00ec618c2f0b1d498f43e5281988339ac082"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e87293717865bca00bbc2046864e33a421468129a8a8fe938abf2397c4a8e26"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7d4313eed1de7def6d2e9c4780f11e1faed3800f2e2ad4de47dc15ba7a40da"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7e5d5aebf87bcacc6bd3f548e0648f4e1b26d4b50e7239df234a42cb1374c039"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b5dc09f6de78fa9f7152e701fdfa2ed827a7c0543a6746ba9879e8731c6da5"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf6053352d053d32a2294510416c704e5b62325a8214dca5c764455e318948c"}, - {file = "css_inline-0.8.7.tar.gz", hash = "sha256:68dac7010c27624627f7df9be12888b9129fd658804f52f8feac25f7d4766050"}, -] - -[[package]] -name = "defusedcsv" -version = "2.0.0" -description = "Drop-in replacement for Python's CSV library that tries to mitigate CSV injection attacks" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "defusedcsv-2.0.0-py3-none-any.whl", hash = "sha256:a7bc3b1ac1ce4f8c6c1e8740466b1b5789b51ca18d918b0099313dc0cdf2cef4"}, - {file = "defusedcsv-2.0.0.tar.gz", hash = "sha256:7612228e54ef1690a19f7aef526709010608e987f9998c89588ef05d9ecfe4d6"}, -] - -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - -[[package]] -name = "dj-static" -version = "0.0.6" -description = "Serve production static files with Django." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "dj-static-0.0.6.tar.gz", hash = "sha256:032ec1c532617922e6e3e956d504a6fb1acce4fc1c7c94612d0fda21828ce8ef"}, -] - -[package.dependencies] -static3 = "*" - -[[package]] -name = "django" -version = "4.1.10" -description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "Django-4.1.10-py3-none-any.whl", hash = "sha256:26d0260c2fb8121009e62ffc548b2398dea2522b6454208a852fb0ef264c206c"}, - {file = "Django-4.1.10.tar.gz", hash = "sha256:56343019a9fd839e2e5bf203daf45f25af79d5bffa4c71d56eae4f4404d82ade"}, -] - -[package.dependencies] -asgiref = ">=3.5.2,<4" -sqlparse = ">=0.2.2" -tzdata = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -argon2 = ["argon2-cffi (>=19.1.0)"] -bcrypt = ["bcrypt"] - -[[package]] -name = "django-appconf" -version = "1.0.5" -description = "A helper class for handling configuration defaults of packaged apps gracefully." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, - {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, -] - -[package.dependencies] -django = "*" - -[[package]] -name = "django-bootstrap3" -version = "23.1" -description = "Bootstrap 3 support for Django projects" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-bootstrap3-23.1.tar.gz", hash = "sha256:7095b7c66a89f3baeb78ea028799ebd795d29739fc8602460422e7c2a194ad30"}, - {file = "django_bootstrap3-23.1-py3-none-any.whl", hash = "sha256:044ff0d34cd6a3928e0b7dc104a06b2f73e71bea237da5e9a477cc047a5d8e21"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-compressor" -version = "4.3.1" -description = "('Compresses linked and inline JavaScript or CSS into single cached files.',)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django_compressor-4.3.1-py2.py3-none-any.whl", hash = "sha256:2c451174acb6f083054af7c8089376599b22d6380bd60311f78ec3fed79acc8e"}, - {file = "django_compressor-4.3.1.tar.gz", hash = "sha256:68858c0da6cc099cc29a022d86c3ba8aed114da9d709eeceb0d7b8181b5f8942"}, -] - -[package.dependencies] -django-appconf = ">=1.0.3" -rcssmin = "1.1.1" -rjsmin = "1.2.1" - -[[package]] -name = "django-countries" -version = "7.5.1" -description = "Provides a country field for Django models." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-countries-7.5.1.tar.gz", hash = "sha256:22915d9b9403932b731622619940a54894a3eb0da9a374e7249c8fc453c122d7"}, - {file = "django_countries-7.5.1-py3-none-any.whl", hash = "sha256:2df707aca7a5e677254bed116cf6021a136ebaccd5c2f46860abd6452bb45521"}, -] - -[package.dependencies] -asgiref = "*" -typing-extensions = "*" - -[package.extras] -dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox"] -maintainer = ["django", "transifex-client", "zest.releaser[recommended]"] -pyuca = ["pyuca"] -test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"] - -[[package]] -name = "django-filter" -version = "23.2" -description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-filter-23.2.tar.gz", hash = "sha256:2fe15f78108475eda525692813205fa6f9e8c1caf1ae65daa5862d403c6dbf00"}, - {file = "django_filter-23.2-py3-none-any.whl", hash = "sha256:d12d8e0fc6d3eb26641e553e5d53b191eb8cec611427d4bdce0becb1f7c172b5"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-formset-js-improved" -version = "0.5.0.3" -description = "Extend Django formsets with JavaScript" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-formset-js-improved-0.5.0.3.tar.gz", hash = "sha256:6c5a0ffec4fc25ad5f502b914fa6414fb0e43ec5f116a88a2da0090a72f32a4c"}, - {file = "django_formset_js_improved-0.5.0.3-py3-none-any.whl", hash = "sha256:99ff030c427fe412a8a54c6fadc3809f3d65683470b67f68d1d421ce7a09bb94"}, -] - -[package.dependencies] -Django = "*" -django-jquery-js = "*" - -[[package]] -name = "django-formtools" -version = "2.4.1" -description = "A set of high-level abstractions for Django forms" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-formtools-2.4.1.tar.gz", hash = "sha256:21f8d5dac737f1e636fa8a0a10969c1c32f525a6dfa27c29592827ba70d9643a"}, - {file = "django_formtools-2.4.1-py3-none-any.whl", hash = "sha256:49ea8a64ddef4728a558bf5f8f622c0f4053b979edcf193bf00dd80432ab2f12"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-hierarkey" -version = "1.1.0" -description = "Hierarchical key-value store for django" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-hierarkey-1.1.0.tar.gz", hash = "sha256:b0963b760b5bb4b97fe7abc6b7a0f3d2dd23e4741ac4585252af1d47f5ae771a"}, - {file = "django_hierarkey-1.1.0-py3-none-any.whl", hash = "sha256:2be56a6ae79bec1b356e2df22d2781fb6a48bd4bc55635a74a3e468fc0e2046f"}, -] - -[package.dependencies] -python-dateutil = "*" - -[[package]] -name = "django-hijack" -version = "3.3.0" -description = "django-hijack allows superusers to hijack (=login as) and work on behalf of another user." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-hijack-3.3.0.tar.gz", hash = "sha256:795cd0087596a21c9bf641fa5cf1787f4edf144e16a423014afbcf2838e8e124"}, - {file = "django_hijack-3.3.0-py3-none-any.whl", hash = "sha256:976ae3bc7e9db0bcb5f5b85a58fba98f5ff1d7a6e92e1b65854289cc79a351d4"}, -] - -[package.dependencies] -django = ">=2.2" - -[package.extras] -test = ["pytest", "pytest-cov", "pytest-django"] - -[[package]] -name = "django-i18nfield" -version = "1.9.4" -description = "Store internationalized strings in Django models" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-i18nfield-1.9.4.tar.gz", hash = "sha256:f24c209f4fcbf407ec1ebf749b6c182bf4089ef46c3cd0e60fa547955d575d2e"}, - {file = "django_i18nfield-1.9.4-py3-none-any.whl", hash = "sha256:d31bf725b43d6fa0d67a403efb45abf54fc6013ee9135ad55679626df9d0c8ca"}, -] - -[[package]] -name = "django-jquery-js" -version = "3.1.1" -description = "jQuery, bundled up so apps can depend upon it" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-jquery-js-3.1.1.tar.gz", hash = "sha256:308e6472801f89be7c02fa3d06bea6470cfbcab8287db80c64b1093717b8eea9"}, -] - -[package.dependencies] -Django = ">=1.4" - -[[package]] -name = "django-libsass" -version = "0.9" -description = "A django-compressor filter to compile SASS files using libsass" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-libsass-0.9.tar.gz", hash = "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5"}, - {file = "django_libsass-0.9-py3-none-any.whl", hash = "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97"}, -] - -[package.dependencies] -django-compressor = ">=1.3" -libsass = ">=0.7.0,<1" - -[[package]] -name = "django-localflavor" -version = "4.0" -description = "Country-specific Django helpers" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-localflavor-4.0.tar.gz", hash = "sha256:11859e522dba74aa6dde5a659242b1fbc5efb4dea08e9b77315402bdeca5194e"}, - {file = "django_localflavor-4.0-py3-none-any.whl", hash = "sha256:7a5b1df03ca8e10df9d1b3c2e4314e43383067868183cdf41ab4e7a973694a8b"}, -] - -[package.dependencies] -django = ">=3.2" -python-stdnum = ">=1.6" - -[[package]] -name = "django-markup" -version = "1.7.2" -description = "A generic Django application to convert text with specific markup to html." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-markup-1.7.2.tar.gz", hash = "sha256:6cd09ecc701cd80d658a8eeeb0668b0450176bc0962f8c01dd26d64cbeacc945"}, - {file = "django_markup-1.7.2-py2.py3-none-any.whl", hash = "sha256:c6473fa6c8047abcb94e12201a289c2bb06c340c61526d60eef5de53e26750bf"}, -] - -[package.dependencies] -django = ">=3.2" - -[package.extras] -all-filter-dependencies = ["bleach (>=3.0)", "docutils (>=0.14)", "markdown (>=2.6.9)", "pygments (>=2.10.0)", "pygments (>=2.2.0)", "python-creole (>=1.3.1)", "smartypants (>=2.0.0)", "textile (>=2.3.16)"] - -[[package]] -name = "django-oauth-toolkit" -version = "2.2.0" -description = "OAuth2 Provider for Django" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-oauth-toolkit-2.2.0.tar.gz", hash = "sha256:46890decb24a34e2a5382debeaf7752e50d90b7a11716cf2a9fd067097ec0963"}, - {file = "django_oauth_toolkit-2.2.0-py3-none-any.whl", hash = "sha256:abd85c74af525a62365ec2049113e73a2ff8b46ef906e7104a7ba968ef02a11d"}, -] - -[package.dependencies] -django = ">=2.2,<4.0.0 || >4.0.0" -jwcrypto = ">=0.8.0" -oauthlib = ">=3.1.0" -requests = ">=2.13.0" - -[[package]] -name = "django-otp" -version = "1.2.2" -description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django_otp-1.2.2-py3-none-any.whl", hash = "sha256:90765d5dac238a719f9550ac05681dd6307f513a81a10b6adb879b4abc6bc1a3"}, - {file = "django_otp-1.2.2.tar.gz", hash = "sha256:007a6354dabb3a1a54574bf73abf045ebbde0bb8734a38e2ed7845ba450f345e"}, -] - -[package.dependencies] -django = ">=3.2" - -[package.extras] -qrcode = ["qrcode"] - -[[package]] -name = "django-phonenumber-field" -version = "7.1.0" -description = "An international phone number field for django models." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-phonenumber-field-7.1.0.tar.gz", hash = "sha256:63721dbdc7424cd594a08d80f550e790cf6e7c903cbc0fb4dd9d86baac8b8c51"}, - {file = "django_phonenumber_field-7.1.0-py3-none-any.whl", hash = "sha256:4eaab35fe9a163046dc3a47188771385c56a788e0e11b7bbcc662e1e6b7b9104"}, -] - -[package.dependencies] -Django = ">=3.2" - -[package.extras] -phonenumbers = ["phonenumbers (>=7.0.2)"] -phonenumberslite = ["phonenumberslite (>=7.0.2)"] - -[[package]] -name = "django-redis" -version = "5.2.0" -description = "Full featured redis cache backend for Django." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-redis-5.2.0.tar.gz", hash = "sha256:8a99e5582c79f894168f5865c52bd921213253b7fd64d16733ae4591564465de"}, - {file = "django_redis-5.2.0-py3-none-any.whl", hash = "sha256:1d037dc02b11ad7aa11f655d26dac3fb1af32630f61ef4428860a2e29ff92026"}, -] - -[package.dependencies] -Django = ">=2.2" -redis = ">=3,<4.0.0 || >4.0.0,<4.0.1 || >4.0.1" - -[package.extras] -hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] - -[[package]] -name = "django-scopes" -version = "2.0.0" -description = "Scope querys in multi-tenant django applications" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-scopes-2.0.0.tar.gz", hash = "sha256:d190d9a2462bce812bc6fdd254e47ba031f6fba3279c8ac7397c671df0a4e54f"}, - {file = "django_scopes-2.0.0-py3-none-any.whl", hash = "sha256:9cf521b4d543ffa2ff6369fb5a1dda03567e862ba89626c01405f3d93ca04724"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-statici18n" -version = "2.3.1" -description = "A Django app that compiles i18n JavaScript catalogs to static files." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-statici18n-2.3.1.tar.gz", hash = "sha256:00079579035d5b45320830191e2c047f8643b7906307eff9833f0fa95068a603"}, - {file = "django_statici18n-2.3.1-py2.py3-none-any.whl", hash = "sha256:5f4bb3d58670def2df490babe338524927cfb2ebe2e5e20538b98d9424e83d0e"}, -] - -[package.dependencies] -Django = ">=2.2" -django-appconf = ">=1.0" - -[[package]] -name = "djangorestframework" -version = "3.14.0" -description = "Web APIs for Django, made easy." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, - {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, -] - -[package.dependencies] -django = ">=3.0" -pytz = "*" - -[[package]] -name = "dnspython" -version = "2.3.0" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, - {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, -] - -[package.extras] -curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<40.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] -doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.23)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "drf-ujson2" -version = "1.7.2" -description = "Django Rest Framework UJSON Renderer" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "drf_ujson2-1.7.2-py3-none-any.whl", hash = "sha256:5fead7ee1cccafd08137a845ddb1f153415519519ddfd9de5bcadfacb70ceb0b"}, - {file = "drf_ujson2-1.7.2.tar.gz", hash = "sha256:ae550e861280e7166232ccbfcb4e950059d88f7123abb02019bed48f8e2dbdbb"}, -] - -[package.dependencies] -django = "*" -djangorestframework = "*" -ujson = ">=2.0.1" - -[package.extras] -dev = ["pytest", "pytest-cov", "pytest-django", "pytest-mock", "pytest-runner"] - -[[package]] -name = "elementpath" -version = "4.1.4" -description = "XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "elementpath-4.1.4-py3-none-any.whl", hash = "sha256:e7c6d25546dfb381a2c9cde3b78c0c40f52811e06eb810faf019e16c531a74bf"}, - {file = "elementpath-4.1.4.tar.gz", hash = "sha256:f991c42ff66fa06e219141ccf65890261e6635b448e7d4c2d8b62dc5bf1de9e8"}, -] - -[package.extras] -dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy", "tox", "xmlschema (>=2.0.0)"] - -[[package]] -name = "enum34" -version = "1.1.10" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, - {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, - {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, -] - -[[package]] -name = "et-xmlfile" -version = "1.1.0" -description = "An implementation of lxml.xmlfile for the standard library" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, - {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, -] - -[[package]] -name = "frozenlist" -version = "1.3.3" -description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, - {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, - {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, - {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, - {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, - {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, - {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, - {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, - {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, - {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, - {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, -] - -[[package]] -name = "future" -version = "0.18.3" -description = "Clean single-source support for Python 3 and 2" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, -] - -[[package]] -name = "geoip2" -version = "4.7.0" -description = "MaxMind GeoIP2 API" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "geoip2-4.7.0-py2.py3-none-any.whl", hash = "sha256:078fcd4cce26ea029b1e3252a0f0ec20a1f42e7ab0f19b7be3864f20f4db2b51"}, - {file = "geoip2-4.7.0.tar.gz", hash = "sha256:3bdde4994f6bc917eafab5b51e772d737b2ae00037a5b85001fb06dc68f779df"}, -] - -[package.dependencies] -aiohttp = ">=3.6.2,<4.0.0" -maxminddb = ">=2.3.0,<3.0.0" -requests = ">=2.24.0,<3.0.0" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.8.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "isodate" -version = "0.6.1" -description = "An ISO 8601 date/time/duration parser and formatter" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, - {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "isoweek" -version = "1.3.3" -description = "Objects representing a week" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "isoweek-1.3.3-py2.py3-none-any.whl", hash = "sha256:d3324c497d97f1534669de225ec877964222e4cc773a4a99063086f7a4e342b6"}, - {file = "isoweek-1.3.3.tar.gz", hash = "sha256:73f3f7bac443e05a3ab45c32a72048b0c4f26d53d81462ec4b142c7581d3ffe8"}, -] - -[[package]] -name = "jsonschema" -version = "4.18.0" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema-4.18.0-py3-none-any.whl", hash = "sha256:b508dd6142bd03f4c3670534c80af68cd7bbff9ea830b9cf2625d4a3c49ddf60"}, - {file = "jsonschema-4.18.0.tar.gz", hash = "sha256:8caf5b57a990a98e9b39832ef3cb35c176fe331414252b6e1b26fd5866f891a4"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jsonschema-specifications" -version = "2023.6.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema_specifications-2023.6.1-py3-none-any.whl", hash = "sha256:3d2b82663aff01815f744bb5c7887e2121a63399b49b104a3c96145474d091d7"}, - {file = "jsonschema_specifications-2023.6.1.tar.gz", hash = "sha256:ca1c4dd059a9e7b34101cf5b3ab7ff1d18b139f35950d598d629837ef66e8f28"}, -] - -[package.dependencies] -referencing = ">=0.28.0" - -[[package]] -name = "jwcrypto" -version = "1.5.0" -description = "Implementation of JOSE Web standards" -category = "main" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "jwcrypto-1.5.0.tar.gz", hash = "sha256:2c1dc51cf8e38ddf324795dfe9426dee9dd46caf47f535ccbc18781fba810b8d"}, -] - -[package.dependencies] -cryptography = ">=3.4" -deprecated = "*" - -[[package]] -name = "kombu" -version = "5.3.1" -description = "Messaging library for Python." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "kombu-5.3.1-py3-none-any.whl", hash = "sha256:48ee589e8833126fd01ceaa08f8a2041334e9f5894e5763c8486a550454551e9"}, - {file = "kombu-5.3.1.tar.gz", hash = "sha256:fbd7572d92c0bf71c112a6b45163153dea5a7b6a701ec16b568c27d0fd2370f2"}, -] - -[package.dependencies] -amqp = ">=5.1.1,<6.0.0" -vine = "*" - -[package.extras] -azureservicebus = ["azure-servicebus (>=7.10.0)"] -azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] -confluentkafka = ["confluent-kafka (==2.1.1)"] -consul = ["python-consul2"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -mongodb = ["pymongo (>=4.1.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] -qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=4.5.2)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] -sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=2.8.0)"] - -[[package]] -name = "libsass" -version = "0.22.0" -description = "Sass for Python: A straightforward binding of libsass for Python." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "libsass-0.22.0-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f1efc1b612299c88aec9e39d6ca0c266d360daa5b19d9430bdeaffffa86993f9"}, - {file = "libsass-0.22.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:081e256ab3c5f3f09c7b8dea3bf3bf5e64a97c6995fd9eea880639b3f93a9f9a"}, - {file = "libsass-0.22.0-cp37-abi3-win32.whl", hash = "sha256:89c5ce497fcf3aba1dd1b19aae93b99f68257e5f2026b731b00a872f13324c7f"}, - {file = "libsass-0.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:65455a2728b696b62100eb5932604aa13a29f4ac9a305d95773c14aaa7200aaf"}, - {file = "libsass-0.22.0.tar.gz", hash = "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425"}, -] - -[[package]] -name = "lxml" -version = "4.9.3" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -files = [ - {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, - {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, - {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, - {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, - {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, - {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, -] - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.35)"] - -[[package]] -name = "markdown" -version = "3.4.3" -description = "Python implementation of John Gruber's Markdown." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Markdown-3.4.3-py3-none-any.whl", hash = "sha256:065fd4df22da73a625f14890dd77eb8040edcbd68794bcd35943be14490608b2"}, - {file = "Markdown-3.4.3.tar.gz", hash = "sha256:8bf101198e004dc93e84a12a7395e31aac6a9c9942848ae1d99b9d72cf9b3520"}, -] - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "maxminddb" -version = "2.4.0" -description = "Reader for the MaxMind DB format" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "maxminddb-2.4.0.tar.gz", hash = "sha256:81e54e53408bd502650e5969ccba16780af659ec1db1c44b2c997e4330a5ed96"}, -] - -[[package]] -name = "mt-940" -version = "4.30.0" -description = "A library to parse MT940 files and returns smart Python collections for statistics and manipulation." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "mt-940-4.30.0.tar.gz", hash = "sha256:da14e2ec84d6c6e85604d2a6b8a800b08ec6000351acde501c41eb662819295c"}, - {file = "mt_940-4.30.0-py2.py3-none-any.whl", hash = "sha256:66fa0c1e942478fd7970b791be017b1ca46556f54d44115008f3982e97ae0a9b"}, -] - -[package.extras] -docs = ["GitPython (>=2.1.9)", "sphinx (>=1.7.2)", "sphinx2rst"] -tests = ["flake8", "pytest", "pytest-cache", "pytest-cover", "pytest-flake8", "pyyaml"] - -[[package]] -name = "multidict" -version = "6.0.4" -description = "multidict implementation" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, -] - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "openpyxl" -version = "3.1.2" -description = "A Python library to read/write Excel 2010 xlsx/xlsm files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, -] - -[package.dependencies] -et-xmlfile = "*" - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "paypal-checkout-serversdk" -version = "1.0.1" -description = "Python Rest SDK for PayPal Checkout" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypal-checkout-serversdk-1.0.1.tar.gz", hash = "sha256:80f62ba2d9fe22b58c2ce1f310146acf6037088493398dba8b1bb67b493aee5e"}, - {file = "paypal_checkout_serversdk-1.0.1-py2-none-any.whl", hash = "sha256:e82bf50c249d7383cb4f68d8562a68dde3e7089389f286c111b6689bd3fdad36"}, -] - -[package.dependencies] -paypalhttp = "*" - -[[package]] -name = "paypalhttp" -version = "1.0.1" -description = "" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypalhttp-1.0.1-py3-none-any.whl", hash = "sha256:251a6e72e2c5140c5372ee6351b64f7af61b5aad9c554618db5782a06205989a"}, - {file = "paypalhttp-1.0.1.tar.gz", hash = "sha256:20e00f95ea052f59145b65bc2baf3b8720f449460c96bf7d32f191c8e293d16d"}, -] - -[package.dependencies] -pyopenssl = ">=0.15" -requests = ">=2.0.0" -six = ">=1.0.0" - -[[package]] -name = "paypalrestsdk" -version = "1.13.1" -description = "The PayPal REST SDK provides Python APIs to create, process and manage payments." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypalrestsdk-1.13.1.tar.gz", hash = "sha256:238713208031e8981bf70b3350b3d7f85ed64d34e0f21e4c1184444a546fee7f"}, -] - -[package.dependencies] -pyopenssl = ">=0.15" -requests = ">=1.0.0" -six = ">=1.0.0" - -[[package]] -name = "phonenumberslite" -version = "8.13.15" -description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "phonenumberslite-8.13.15-py2.py3-none-any.whl", hash = "sha256:87434233270bee76d6624f9e256688fa4c2ecb1b235f7c9275446b8978f7de8d"}, - {file = "phonenumberslite-8.13.15.tar.gz", hash = "sha256:35e48d547c38b9e2d62d77a2f91ef628a98f184c474ce453dff001978312a16b"}, -] - -[[package]] -name = "pillow" -version = "9.5.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, - {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, - {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, - {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, - {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, - {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, - {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, - {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, - {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, - {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, - {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, - {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, - {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, - {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, - {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, - {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, - {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "platformdirs" -version = "3.8.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"}, - {file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"}, -] - -[package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] - -[[package]] -name = "ply" -version = "3.11" -description = "Python Lex & Yacc" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, - {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, -] - -[[package]] -name = "pretix" -version = "2023.6.0" -description = "Reinventing presales, one ticket at a time" -category = "main" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pretix-2023.6.0-py3-none-any.whl", hash = "sha256:0365d050fbbd3302439778367c16bd16c590507bad5337872787b15a2185e2b6"}, - {file = "pretix-2023.6.0.tar.gz", hash = "sha256:6bc6cbfe4e3ccf4388e4d829bcdba319b649007e02d9d79e9d58cc5e8380b290"}, -] - -[package.dependencies] -arabic-reshaper = "3.0.0" -babel = "*" -BeautifulSoup4 = ">=4.12.0,<4.13.0" -bleach = ">=5.0.0,<5.1.0" -celery = ">=5.3.0,<5.4.0" -chardet = ">=5.1.0,<5.2.0" -cryptography = ">=3.4.2" -css-inline = ">=0.8.0,<0.9.0" -defusedcsv = ">=1.1.0" -dj-static = "*" -Django = ">=4.1.0,<4.2.0" -django-bootstrap3 = ">=23.1.0,<23.2.0" -django-compressor = ">=4.3.0,<4.4.0" -django-countries = ">=7.5.0,<7.6.0" -django-filter = "23.2" -django-formset-js-improved = "0.5.0.3" -django-formtools = "2.4.1" -django-hierarkey = ">=1.1.0,<1.2.0" -django-hijack = ">=3.3.0,<3.4.0" -django-i18nfield = ">=1.9.4,<1.10.0" -django-libsass = "0.9" -django-localflavor = "4.0" -django-markup = "*" -django-oauth-toolkit = ">=2.2.0,<2.3.0" -django-otp = ">=1.2.0,<1.3.0" -django-phonenumber-field = ">=7.1.0,<7.2.0" -django-redis = ">=5.2.0,<5.3.0" -django-scopes = ">=2.0.0,<2.1.0" -django-statici18n = ">=2.3.0,<2.4.0" -djangorestframework = ">=3.14.0,<3.15.0" -dnspython = ">=2.3.0,<2.4.0" -drf-ujson2 = ">=1.7.0,<1.8.0" -geoip2 = ">=4.0.0,<5.0.0" -importlib-metadata = ">=6.0.0,<7.0.0" -isoweek = "*" -jsonschema = "*" -kombu = ">=5.3.0,<5.4.0" -libsass = ">=0.22.0,<0.23.0" -lxml = "*" -markdown = "3.4.3" -mt-940 = ">=4.30.0,<4.31.0" -oauthlib = ">=3.2.0,<3.3.0" -openpyxl = ">=3.1.0,<3.2.0" -packaging = "*" -paypal-checkout-serversdk = ">=1.0.0,<1.1.0" -paypalrestsdk = ">=1.13.0,<1.14.0" -phonenumberslite = ">=8.13.0,<8.14.0" -Pillow = ">=9.5.0,<9.6.0" -pretix-plugin-build = "*" -protobuf = ">=4.23.0,<4.24.0" -psycopg2-binary = "*" -pycountry = "*" -pycparser = "2.21" -pycryptodome = ">=3.18.0,<3.19.0" -PyJWT = ">=2.7.0,<2.8.0" -pypdf = ">=3.9.0,<3.10.0" -python-bidi = ">=0.4.0,<0.5.0" -python-dateutil = ">=2.8.0,<2.9.0" -python-u2flib-server = ">=4.0.0,<5.0.0" -pytz = "*" -pytz-deprecation-shim = ">=0.1.0,<0.2.0" -pyuca = "*" -qrcode = ">=7.4.0,<7.5.0" -redis = ">=4.5.4,<4.6.0" -reportlab = ">=4.0.0,<4.1.0" -requests = ">=2.31.0,<2.32.0" -sentry-sdk = ">=1.15.0,<1.16.0" -sepaxml = ">=2.6.0,<2.7.0" -slimit = "*" -static3 = ">=0.7.0,<0.8.0" -stripe = ">=5.4.0,<5.5.0" -text-unidecode = ">=1.0.0,<2.0.0" -tlds = ">=2020041600" -tqdm = ">=4.0.0,<5.0.0" -vat-moss-forked = "2020.3.20.0.11.0" -vobject = ">=0.9.0,<0.10.0" -webauthn = ">=0.4.0,<0.5.0" -zeep = ">=4.2.0,<4.3.0" - -[package.extras] -dev = ["coverage", "coveralls", "flake8 (>=6.0.0,<6.1.0)", "freezegun", "isort (>=5.12.0,<5.13.0)", "pep8-naming (>=0.13.0,<0.14.0)", "potypo", "pycodestyle (>=2.10.0,<2.11.0)", "pyflakes (>=3.0.0,<3.1.0)", "pytest (>=7.3.0,<7.4.0)", "pytest-cache", "pytest-cov", "pytest-django (>=4.0.0,<5.0.0)", "pytest-mock (>=3.10.0,<3.11.0)", "pytest-rerunfailures (>=11.0.0,<12.0.0)", "pytest-sugar", "pytest-xdist (>=3.3.0,<3.4.0)", "responses"] -memcached = ["pylibmc"] - -[[package]] -name = "pretix-plugin-build" -version = "1.0.1" -description = "Build toolchain for pretix plugins" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pretix-plugin-build-1.0.1.tar.gz", hash = "sha256:88b6ea70201b78ae0fc8b5e279ba5d136eebb7a6ce3fb797733206d9b76fbd8a"}, - {file = "pretix_plugin_build-1.0.1-py3-none-any.whl", hash = "sha256:9a7be6b373471a274069f021148f918ab2b91bf5addca0e871f63c0451c57fc4"}, -] - -[package.dependencies] -django = "*" - -[[package]] -name = "prompt-toolkit" -version = "3.0.39" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"}, - {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "4.23.4" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "protobuf-4.23.4-cp310-abi3-win32.whl", hash = "sha256:5fea3c64d41ea5ecf5697b83e41d09b9589e6f20b677ab3c48e5f242d9b7897b"}, - {file = "protobuf-4.23.4-cp310-abi3-win_amd64.whl", hash = "sha256:7b19b6266d92ca6a2a87effa88ecc4af73ebc5cfde194dc737cf8ef23a9a3b12"}, - {file = "protobuf-4.23.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8547bf44fe8cec3c69e3042f5c4fb3e36eb2a7a013bb0a44c018fc1e427aafbd"}, - {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fee88269a090ada09ca63551bf2f573eb2424035bcf2cb1b121895b01a46594a"}, - {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:effeac51ab79332d44fba74660d40ae79985901ac21bca408f8dc335a81aa597"}, - {file = "protobuf-4.23.4-cp37-cp37m-win32.whl", hash = "sha256:c3e0939433c40796ca4cfc0fac08af50b00eb66a40bbbc5dee711998fb0bbc1e"}, - {file = "protobuf-4.23.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9053df6df8e5a76c84339ee4a9f5a2661ceee4a0dab019e8663c50ba324208b0"}, - {file = "protobuf-4.23.4-cp38-cp38-win32.whl", hash = "sha256:e1c915778d8ced71e26fcf43c0866d7499891bca14c4368448a82edc61fdbc70"}, - {file = "protobuf-4.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:351cc90f7d10839c480aeb9b870a211e322bf05f6ab3f55fcb2f51331f80a7d2"}, - {file = "protobuf-4.23.4-cp39-cp39-win32.whl", hash = "sha256:6dd9b9940e3f17077e820b75851126615ee38643c2c5332aa7a359988820c720"}, - {file = "protobuf-4.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:0a5759f5696895de8cc913f084e27fd4125e8fb0914bb729a17816a33819f474"}, - {file = "protobuf-4.23.4-py3-none-any.whl", hash = "sha256:e9d0be5bf34b275b9f87ba7407796556abeeba635455d036c7351f7c183ef8ff"}, - {file = "protobuf-4.23.4.tar.gz", hash = "sha256:ccd9430c0719dce806b93f89c91de7977304729e55377f872a92465d548329a9"}, -] - -[[package]] -name = "psycopg2-binary" -version = "2.9.6" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "psycopg2-binary-2.9.6.tar.gz", hash = "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-win32.whl", hash = "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b"}, - {file = "psycopg2_binary-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-win32.whl", hash = "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb"}, - {file = "psycopg2_binary-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6"}, - {file = "psycopg2_binary-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0"}, - {file = "psycopg2_binary-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e"}, - {file = "psycopg2_binary-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-win32.whl", hash = "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503"}, - {file = "psycopg2_binary-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-win32.whl", hash = "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848"}, - {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"}, -] - -[[package]] -name = "pycountry" -version = "22.3.5" -description = "ISO country, subdivision, language, currency and script definitions and their translations" -category = "main" -optional = false -python-versions = ">=3.6, <4" -files = [ - {file = "pycountry-22.3.5.tar.gz", hash = "sha256:b2163a246c585894d808f18783e19137cb70a0c18fb36748dc01fc6f109c1646"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pycryptodome" -version = "3.18.0" -description = "Cryptographic library for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycryptodome-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:d1497a8cd4728db0e0da3c304856cb37c0c4e3d0b36fcbabcc1600f18504fc54"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:928078c530da78ff08e10eb6cada6e0dff386bf3d9fa9871b4bbc9fbc1efe024"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:157c9b5ba5e21b375f052ca78152dd309a09ed04703fd3721dce3ff8ecced148"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:d20082bdac9218649f6abe0b885927be25a917e29ae0502eaf2b53f1233ce0c2"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:e8ad74044e5f5d2456c11ed4cfd3e34b8d4898c0cb201c4038fe41458a82ea27"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:62a1e8847fabb5213ccde38915563140a5b338f0d0a0d363f996b51e4a6165cf"}, - {file = "pycryptodome-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:16bfd98dbe472c263ed2821284118d899c76968db1a6665ade0c46805e6b29a4"}, - {file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7a3d22c8ee63de22336679e021c7f2386f7fc465477d59675caa0e5706387944"}, - {file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:78d863476e6bad2a592645072cc489bb90320972115d8995bcfbee2f8b209918"}, - {file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:b6a610f8bfe67eab980d6236fdc73bfcdae23c9ed5548192bb2d530e8a92780e"}, - {file = "pycryptodome-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:422c89fd8df8a3bee09fb8d52aaa1e996120eafa565437392b781abec2a56e14"}, - {file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:9ad6f09f670c466aac94a40798e0e8d1ef2aa04589c29faa5b9b97566611d1d1"}, - {file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:53aee6be8b9b6da25ccd9028caf17dcdce3604f2c7862f5167777b707fbfb6cb"}, - {file = "pycryptodome-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:10da29526a2a927c7d64b8f34592f461d92ae55fc97981aab5bbcde8cb465bb6"}, - {file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f21efb8438971aa16924790e1c3dba3a33164eb4000106a55baaed522c261acf"}, - {file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4944defabe2ace4803f99543445c27dd1edbe86d7d4edb87b256476a91e9ffa4"}, - {file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:51eae079ddb9c5f10376b4131be9589a6554f6fd84f7f655180937f611cd99a2"}, - {file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:83c75952dcf4a4cebaa850fa257d7a860644c70a7cd54262c237c9f2be26f76e"}, - {file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:957b221d062d5752716923d14e0926f47670e95fead9d240fa4d4862214b9b2f"}, - {file = "pycryptodome-3.18.0-cp35-abi3-win32.whl", hash = "sha256:795bd1e4258a2c689c0b1f13ce9684fa0dd4c0e08680dcf597cf9516ed6bc0f3"}, - {file = "pycryptodome-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:b1d9701d10303eec8d0bd33fa54d44e67b8be74ab449052a8372f12a66f93fb9"}, - {file = "pycryptodome-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:cb1be4d5af7f355e7d41d36d8eec156ef1382a88638e8032215c215b82a4b8ec"}, - {file = "pycryptodome-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:fc0a73f4db1e31d4a6d71b672a48f3af458f548059aa05e83022d5f61aac9c08"}, - {file = "pycryptodome-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f022a4fd2a5263a5c483a2bb165f9cb27f2be06f2f477113783efe3fe2ad887b"}, - {file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:363dd6f21f848301c2dcdeb3c8ae5f0dee2286a5e952a0f04954b82076f23825"}, - {file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12600268763e6fec3cefe4c2dcdf79bde08d0b6dc1813887e789e495cb9f3403"}, - {file = "pycryptodome-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4604816adebd4faf8810782f137f8426bf45fee97d8427fa8e1e49ea78a52e2c"}, - {file = "pycryptodome-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:01489bbdf709d993f3058e2996f8f40fee3f0ea4d995002e5968965fa2fe89fb"}, - {file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3811e31e1ac3069988f7a1c9ee7331b942e605dfc0f27330a9ea5997e965efb2"}, - {file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4b967bb11baea9128ec88c3d02f55a3e338361f5e4934f5240afcb667fdaec"}, - {file = "pycryptodome-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9c8eda4f260072f7dbe42f473906c659dcbadd5ae6159dfb49af4da1293ae380"}, - {file = "pycryptodome-3.18.0.tar.gz", hash = "sha256:c9adee653fc882d98956e33ca2c1fb582e23a8af7ac82fee75bd6113c55a0413"}, -] - -[[package]] -name = "pyjwt" -version = "2.7.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "PyJWT-2.7.0-py3-none-any.whl", hash = "sha256:ba2b425b15ad5ef12f200dc67dd56af4e26de2331f965c5439994dad075876e1"}, - {file = "PyJWT-2.7.0.tar.gz", hash = "sha256:bd6ca4a3c4285c1a2d4349e5a035fdf8fb94e04ccd0fcbe6ba289dae9cc3e074"}, -] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pyopenssl" -version = "23.2.0" -description = "Python wrapper module around the OpenSSL library" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyOpenSSL-23.2.0-py3-none-any.whl", hash = "sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2"}, - {file = "pyOpenSSL-23.2.0.tar.gz", hash = "sha256:276f931f55a452e7dea69c7173e984eb2a4407ce413c918aa34b55f82f9b8bac"}, -] - -[package.dependencies] -cryptography = ">=38.0.0,<40.0.0 || >40.0.0,<40.0.1 || >40.0.1,<42" - -[package.extras] -docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] -test = ["flaky", "pretend", "pytest (>=3.0.1)"] - -[[package]] -name = "pypdf" -version = "3.9.1" -description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pypdf-3.9.1-py3-none-any.whl", hash = "sha256:5f4abdb4691a8d7631e7f2db09f66cfe3a388a072882d8375c6b1bdc28027c0a"}, - {file = "pypdf-3.9.1.tar.gz", hash = "sha256:c2b7fcfe25fbd04e8da600cb2700267ecee7e8781dc798cce3a4f567143a4df1"}, -] - -[package.extras] -crypto = ["PyCryptodome"] -dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"] -docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] -full = ["Pillow", "PyCryptodome"] -image = ["Pillow"] - -[[package]] -name = "pypng" -version = "0.20220715.0" -description = "Pure Python library for saving and loading PNG images" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"}, - {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, -] - -[[package]] -name = "python-bidi" -version = "0.4.2" -description = "Pure python implementation of the BiDi layout algorithm" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-bidi-0.4.2.tar.gz", hash = "sha256:5347f71e82b3e9976dc657f09ded2bfe39ba8d6777ca81a5b2c56c30121c496e"}, - {file = "python_bidi-0.4.2-py2.py3-none-any.whl", hash = "sha256:50eef6f6a0bbdd685f9e8c207f3c9050f5b578d0a46e37c76a9c4baea2cc2e13"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-stdnum" -version = "1.18" -description = "Python module to handle standardized numbers and codes" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-stdnum-1.18.tar.gz", hash = "sha256:bcc763d9c49ae23da5d2b7a686d5fd1deec9d9051341160a10d1ac723a26bec0"}, - {file = "python_stdnum-1.18-py2.py3-none-any.whl", hash = "sha256:d7f2a3c7ef4635c957b9cbdd9b1993d1f6ee3a2959f03e172c45440d99f296eb"}, -] - -[package.extras] -soap = ["zeep"] -soap-alt = ["suds"] -soap-fallback = ["PySimpleSOAP"] - -[[package]] -name = "python-u2flib-server" -version = "4.0.1" -description = "Python based U2F server library" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-u2flib-server-4.0.1.tar.gz", hash = "sha256:160425fe00407b06ce261a7d3c455a6a529ed73f71cfea1b436b573e1dff000b"}, -] - -[package.dependencies] -cryptography = ">=1.2" -enum34 = "*" - -[package.extras] -u2f-server = ["WebOb", "argparse"] -yubiauth-server = ["WebOb", "yubiauth"] - -[[package]] -name = "pytz" -version = "2023.3" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, - {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, -] - -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] - -[package.dependencies] -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - -[[package]] -name = "pyuca" -version = "1.2" -description = "a Python implementation of the Unicode Collation Algorithm" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pyuca-1.2-py2.py3-none-any.whl", hash = "sha256:abaa12e1bd2c7c68ca8396ff8383bc0654a739cef3ae68fd7af58bf29af0a91e"}, - {file = "pyuca-1.2.tar.gz", hash = "sha256:8a382fe74627f08c0d18908c0713ca4a20aad5385f077579e56208beea2893b2"}, -] - -[[package]] -name = "qrcode" -version = "7.4.2" -description = "QR Code image generator" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"}, - {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -pypng = "*" -typing-extensions = "*" - -[package.extras] -all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"] -dev = ["pytest", "pytest-cov", "tox"] -maintainer = ["zest.releaser[recommended]"] -pil = ["pillow (>=9.1.0)"] -test = ["coverage", "pytest"] - -[[package]] -name = "rcssmin" -version = "1.1.1" -description = "CSS Minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1"}, - {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877"}, - {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f"}, - {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0afc6e7b64ef30d6dcde88830ec1a237b9f16a39f920a8fd159928684ccf8db"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:705c9112d0ed54ea40aecf97e7fd29bdf0f1c46d278a32d8f957f31dde90778a"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f7a1fcdbafaacac0530da04edca4a44303baab430ea42e7d59aece4b3f3e9a51"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:cf74d7ea5e191f0f344b354eed8b7c83eeafbd9a97bec3a579c3d26edf11b005"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:908fe072efd2432fb0975a61124609a8e05021367f6a3463d45f5e3e74c4fdda"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:35da6a6999e9e2c5b0e691b42ed56cc479373e0ecab33ef5277dfecce625e44a"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e923c105100ab70abde1c01d3196ddd6b07255e32073685542be4e3a60870c8e"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:868215e1fd0e92a6122e0ed5973dfc7bb8330fe1e92274d05b2585253b38c0ca"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c7728e3b546b1b6ea08cab721e8e21409dbcc11b881d0b87d10b0be8930af2a2"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:271e3d2f8614a6d4637ed8fff3d90007f03e2a654cd9444f37d888797662ba72"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:42576d95dfad53d77df2e68dfdec95b89b10fad320f241f1af3ca1438578254a"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:79421230dd67c37ec61ed9892813d2b839b68f2f48ef55c75f976e81701d60b4"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8fcfd10ae2a1c4ce231a33013f2539e07c3836bf17cc945cc25cc30bf8e68e45"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c30f8bc839747b6da59274e0c6e4361915d66532e26448d589cb2b1846d7bf11"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ee386bec6d62f8c814d65c011d604a7c82d24aa3f718facd66e850eea8d6a5a1"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8a26fec3c1e6b7a3765ccbaccc20fbb5c0ed3422cc381e01a2607f08d7621c44"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a04d58a2a21e9a089306d3f99c4b12bf5b656a79c198ef2321e80f8fd9afab06"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:914e589f40573035006913861ed2adc28fbe70082a8b6bff5be7ee430b7b5c2e"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019"}, - {file = "rcssmin-1.1.1.tar.gz", hash = "sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64"}, -] - -[[package]] -name = "redis" -version = "4.5.5" -description = "Python client for Redis database and key-value store" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "redis-4.5.5-py3-none-any.whl", hash = "sha256:77929bc7f5dab9adf3acba2d3bb7d7658f1e0c2f1cafe7eb36434e751c471119"}, - {file = "redis-4.5.5.tar.gz", hash = "sha256:dc87a0bdef6c8bfe1ef1e1c40be7034390c2ae02d92dcd0c7ca1729443899880"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} - -[package.extras] -hiredis = ["hiredis (>=1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] - -[[package]] -name = "referencing" -version = "0.29.1" -description = "JSON Referencing + Python" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "referencing-0.29.1-py3-none-any.whl", hash = "sha256:d3c8f323ee1480095da44d55917cfb8278d73d6b4d5f677e3e40eb21314ac67f"}, - {file = "referencing-0.29.1.tar.gz", hash = "sha256:90cb53782d550ba28d2166ef3f55731f38397def8832baac5d45235f1995e35e"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - -[[package]] -name = "reportlab" -version = "4.0.4" -description = "The Reportlab Toolkit" -category = "main" -optional = false -python-versions = ">=3.7,<4" -files = [ - {file = "reportlab-4.0.4-py3-none-any.whl", hash = "sha256:3dcda79ce04baf70721e2ec54854722644262cac2feec3d5c4c5e77015504cb0"}, -] - -[package.dependencies] -pillow = ">=9.0.0" - -[package.extras] -accel = ["rl-accel (>=0.9.0,<1.1)"] -pycairo = ["freetype-py (>=2.3.0,<2.4)", "rlPyCairo (>=0.2.0,<1)"] -renderpm = ["rl-renderPM (>=4.0.3,<4.1)"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-file" -version = "1.5.1" -description = "File transport adapter for Requests" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, - {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, -] - -[package.dependencies] -requests = ">=1.0.0" -six = "*" - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -description = "A utility belt for advanced users of python-requests" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, - {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, -] - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -name = "rjsmin" -version = "1.2.1" -description = "Javascript Minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1"}, - {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064"}, - {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346"}, - {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bd1faedc425006d9e86b23837d164f01d105b7a8b66b767a9766d0014773db2a"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:99c074cd6a8302ff47118a9c3d086f89328dc8e5c4b105aa1f348fb85c765a30"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:bc5bc2f94e59bc81562c572b7f1bdd6bcec4f61168dc68a2993bad2d355b6e19"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:35f21046504544e2941e04190ce24161255479133751550e36ddb3f4af0ecdca"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:ca90630b84fe94bb07739c3e3793e87d30c6ee450dde08653121f0d9153c8d0d"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7dd58b5ed88233bc61dc80b0ed87b93a1786031d9977c70d335221ef1ac5581a"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:f0895b360dccf7e2d6af8762a52985e3fbaa56778de1bf6b20dbc96134253807"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:747bc9d3bc8a220f40858e6aad50b2ae2eb7f69c924d4fa3803b81be1c1ddd02"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f7cd33602ec0f393a0058e883284496bb4dbbdd34e0bbe23b594c8933ddf9b65"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3453ee6d5e7a2723ec45c2909e2382371783400e8d51952b692884c6d850a3d0"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8c340e251619c97571a5ade20f147f1f7e8664f66a2d6d7319e05e3ef6a4423c"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:145c6af8df42d8af102d0d39a6de2e5fa66aef9e38947cfb9d65377d1b9940b2"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bbd7a0abaa394afd951f5d4e05249d306fec1c9674bfee179787674dddd0bdb7"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:eb770aaf637919b0011c4eb87b9ac6317079fb9800eb17c90dda05fc9de4ebc3"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5d67ec09da46a492186e35cabca02a0d092eda5ef5b408a419b99ee4acf28d5c"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d332e44a1b21ad63401cc7eebc81157e3d982d5fb503bb4faaea5028068d71e9"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:113132a40ce7d03b2ced4fac215f0297338ed1c207394b739266efab7831988b"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:122aa52bcf7ad9f12728d309012d1308c6ecfe4d6b09ea867a110dcad7b7728c"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39"}, - {file = "rjsmin-1.2.1.tar.gz", hash = "sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411"}, -] - -[[package]] -name = "rpds-py" -version = "0.8.10" -description = "Python bindings to Rust's persistent data structures (rpds)" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rpds_py-0.8.10-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:93d06cccae15b3836247319eee7b6f1fdcd6c10dabb4e6d350d27bd0bdca2711"}, - {file = "rpds_py-0.8.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3816a890a6a9e9f1de250afa12ca71c9a7a62f2b715a29af6aaee3aea112c181"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7c6304b894546b5a6bdc0fe15761fa53fe87d28527a7142dae8de3c663853e1"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad3bfb44c8840fb4be719dc58e229f435e227fbfbe133dc33f34981ff622a8f8"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14f1c356712f66653b777ecd8819804781b23dbbac4eade4366b94944c9e78ad"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82bb361cae4d0a627006dadd69dc2f36b7ad5dc1367af9d02e296ec565248b5b"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2e3c4f2a8e3da47f850d7ea0d7d56720f0f091d66add889056098c4b2fd576c"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15a90d0ac11b4499171067ae40a220d1ca3cb685ec0acc356d8f3800e07e4cb8"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70bb9c8004b97b4ef7ae56a2aa56dfaa74734a0987c78e7e85f00004ab9bf2d0"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d64f9f88d5203274a002b54442cafc9c7a1abff2a238f3e767b70aadf919b451"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ccbbd276642788c4376fbe8d4e6c50f0fb4972ce09ecb051509062915891cbf0"}, - {file = "rpds_py-0.8.10-cp310-none-win32.whl", hash = "sha256:fafc0049add8043ad07ab5382ee80d80ed7e3699847f26c9a5cf4d3714d96a84"}, - {file = "rpds_py-0.8.10-cp310-none-win_amd64.whl", hash = "sha256:915031002c86a5add7c6fd4beb601b2415e8a1c956590a5f91d825858e92fe6e"}, - {file = "rpds_py-0.8.10-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:84eb541a44f7a18f07a6bfc48b95240739e93defe1fdfb4f2a295f37837945d7"}, - {file = "rpds_py-0.8.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f59996d0550894affaad8743e97b9b9c98f638b221fac12909210ec3d9294786"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9adb5664b78fcfcd830000416c8cc69853ef43cb084d645b3f1f0296edd9bae"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f96f3f98fbff7af29e9edf9a6584f3c1382e7788783d07ba3721790625caa43e"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:376b8de737401050bd12810003d207e824380be58810c031f10ec563ff6aef3d"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d1c2bc319428d50b3e0fa6b673ab8cc7fa2755a92898db3a594cbc4eeb6d1f7"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73a1e48430f418f0ac3dfd87860e4cc0d33ad6c0f589099a298cb53724db1169"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134ec8f14ca7dbc6d9ae34dac632cdd60939fe3734b5d287a69683c037c51acb"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4b519bac7c09444dd85280fd60f28c6dde4389c88dddf4279ba9b630aca3bbbe"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9cd57981d9fab04fc74438d82460f057a2419974d69a96b06a440822d693b3c0"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:69d089c026f6a8b9d64a06ff67dc3be196707b699d7f6ca930c25f00cf5e30d8"}, - {file = "rpds_py-0.8.10-cp311-none-win32.whl", hash = "sha256:220bdcad2d2936f674650d304e20ac480a3ce88a40fe56cd084b5780f1d104d9"}, - {file = "rpds_py-0.8.10-cp311-none-win_amd64.whl", hash = "sha256:6c6a0225b8501d881b32ebf3f5807a08ad3685b5eb5f0a6bfffd3a6e039b2055"}, - {file = "rpds_py-0.8.10-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e3d0cd3dff0e7638a7b5390f3a53057c4e347f4ef122ee84ed93fc2fb7ea4aa2"}, - {file = "rpds_py-0.8.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d77dff3a5aa5eedcc3da0ebd10ff8e4969bc9541aa3333a8d41715b429e99f47"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41c89a366eae49ad9e65ed443a8f94aee762931a1e3723749d72aeac80f5ef2f"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3793c21494bad1373da517001d0849eea322e9a049a0e4789e50d8d1329df8e7"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:805a5f3f05d186c5d50de2e26f765ba7896d0cc1ac5b14ffc36fae36df5d2f10"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b01b39ad5411563031ea3977bbbc7324d82b088e802339e6296f082f78f6115c"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f1e860be21f3e83011116a65e7310486300e08d9a3028e73e8d13bb6c77292"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a13c8e56c46474cd5958d525ce6a9996727a83d9335684e41f5192c83deb6c58"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:93d99f957a300d7a4ced41615c45aeb0343bb8f067c42b770b505de67a132346"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:148b0b38d719c0760e31ce9285a9872972bdd7774969a4154f40c980e5beaca7"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3cc5e5b5514796f45f03a568981971b12a3570f3de2e76114f7dc18d4b60a3c4"}, - {file = "rpds_py-0.8.10-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:e8e24b210a4deb5a7744971f8f77393005bae7f873568e37dfd9effe808be7f7"}, - {file = "rpds_py-0.8.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b41941583adce4242af003d2a8337b066ba6148ca435f295f31ac6d9e4ea2722"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c490204e16bca4f835dba8467869fe7295cdeaa096e4c5a7af97f3454a97991"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee45cd1d84beed6cbebc839fd85c2e70a3a1325c8cfd16b62c96e2ffb565eca"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a8ca409f1252e1220bf09c57290b76cae2f14723746215a1e0506472ebd7bdf"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96b293c0498c70162effb13100624c5863797d99df75f2f647438bd10cbf73e4"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4627520a02fccbd324b33c7a83e5d7906ec746e1083a9ac93c41ac7d15548c7"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e39d7ab0c18ac99955b36cd19f43926450baba21e3250f053e0704d6ffd76873"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ba9f1d1ebe4b63801977cec7401f2d41e888128ae40b5441270d43140efcad52"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:802f42200d8caf7f25bbb2a6464cbd83e69d600151b7e3b49f49a47fa56b0a38"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d19db6ba816e7f59fc806c690918da80a7d186f00247048cd833acdab9b4847b"}, - {file = "rpds_py-0.8.10-cp38-none-win32.whl", hash = "sha256:7947e6e2c2ad68b1c12ee797d15e5f8d0db36331200b0346871492784083b0c6"}, - {file = "rpds_py-0.8.10-cp38-none-win_amd64.whl", hash = "sha256:fa326b3505d5784436d9433b7980171ab2375535d93dd63fbcd20af2b5ca1bb6"}, - {file = "rpds_py-0.8.10-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7b38a9ac96eeb6613e7f312cd0014de64c3f07000e8bf0004ad6ec153bac46f8"}, - {file = "rpds_py-0.8.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c4d42e83ddbf3445e6514f0aff96dca511421ed0392d9977d3990d9f1ba6753c"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b21575031478609db6dbd1f0465e739fe0e7f424a8e7e87610a6c7f68b4eb16"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:574868858a7ff6011192c023a5289158ed20e3f3b94b54f97210a773f2f22921"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae40f4a70a1f40939d66ecbaf8e7edc144fded190c4a45898a8cfe19d8fc85ea"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37f7ee4dc86db7af3bac6d2a2cedbecb8e57ce4ed081f6464510e537589f8b1e"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:695f642a3a5dbd4ad2ffbbacf784716ecd87f1b7a460843b9ddf965ccaeafff4"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f43ab4cb04bde6109eb2555528a64dfd8a265cc6a9920a67dcbde13ef53a46c8"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a11ab0d97be374efd04f640c04fe5c2d3dabc6dfb998954ea946ee3aec97056d"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:92cf5b3ee60eef41f41e1a2cabca466846fb22f37fc580ffbcb934d1bcab225a"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ceaac0c603bf5ac2f505a78b2dcab78d3e6b706be6596c8364b64cc613d208d2"}, - {file = "rpds_py-0.8.10-cp39-none-win32.whl", hash = "sha256:dd4f16e57c12c0ae17606c53d1b57d8d1c8792efe3f065a37cb3341340599d49"}, - {file = "rpds_py-0.8.10-cp39-none-win_amd64.whl", hash = "sha256:c03a435d26c3999c2a8642cecad5d1c4d10c961817536af52035f6f4ee2f5dd0"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0da53292edafecba5e1d8c1218f99babf2ed0bf1c791d83c0ab5c29b57223068"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d20a8ed227683401cc508e7be58cba90cc97f784ea8b039c8cd01111e6043e0"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97cab733d303252f7c2f7052bf021a3469d764fc2b65e6dbef5af3cbf89d4892"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c398fda6df361a30935ab4c4bccb7f7a3daef2964ca237f607c90e9f3fdf66f"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2eb4b08c45f8f8d8254cdbfacd3fc5d6b415d64487fb30d7380b0d0569837bf1"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7dfb1cbb895810fa2b892b68153c17716c6abaa22c7dc2b2f6dcf3364932a1c"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89c92b74e8bf6f53a6f4995fd52f4bd510c12f103ee62c99e22bc9e05d45583c"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9c0683cb35a9b5881b41bc01d5568ffc667910d9dbc632a1fba4e7d59e98773"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:0eeb2731708207d0fe2619afe6c4dc8cb9798f7de052da891de5f19c0006c315"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:7495010b658ec5b52835f21d8c8b1a7e52e194c50f095d4223c0b96c3da704b1"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c72ebc22e70e04126158c46ba56b85372bc4d54d00d296be060b0db1671638a4"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2cd3045e7f6375dda64ed7db1c5136826facb0159ea982f77d9cf6125025bd34"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:2418cf17d653d24ffb8b75e81f9f60b7ba1b009a23298a433a4720b2a0a17017"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a2edf8173ac0c7a19da21bc68818be1321998528b5e3f748d6ee90c0ba2a1fd"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f29b8c55fd3a2bc48e485e37c4e2df3317f43b5cc6c4b6631c33726f52ffbb3"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a7d20c1cf8d7b3960c5072c265ec47b3f72a0c608a9a6ee0103189b4f28d531"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:521fc8861a86ae54359edf53a15a05fabc10593cea7b3357574132f8427a5e5a"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5c191713e98e7c28800233f039a32a42c1a4f9a001a8a0f2448b07391881036"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:083df0fafe199371206111583c686c985dddaf95ab3ee8e7b24f1fda54515d09"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ed41f3f49507936a6fe7003985ea2574daccfef999775525d79eb67344e23767"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:2614c2732bf45de5c7f9e9e54e18bc78693fa2f635ae58d2895b7965e470378c"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c60528671d9d467009a6ec284582179f6b88651e83367d0ab54cb739021cd7de"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ee744fca8d1ea822480a2a4e7c5f2e1950745477143668f0b523769426060f29"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a38b9f526d0d6cbdaa37808c400e3d9f9473ac4ff64d33d9163fd05d243dbd9b"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60e0e86e870350e03b3e25f9b1dd2c6cc72d2b5f24e070249418320a6f9097b7"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f53f55a8852f0e49b0fc76f2412045d6ad9d5772251dea8f55ea45021616e7d5"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c493365d3fad241d52f096e4995475a60a80f4eba4d3ff89b713bc65c2ca9615"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:300eb606e6b94a7a26f11c8cc8ee59e295c6649bd927f91e1dbd37a4c89430b6"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a665f6f1a87614d1c3039baf44109094926dedf785e346d8b0a728e9cabd27a"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:927d784648211447201d4c6f1babddb7971abad922b32257ab74de2f2750fad0"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c200b30dd573afa83847bed7e3041aa36a8145221bf0cfdfaa62d974d720805c"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:08166467258fd0240a1256fce272f689f2360227ee41c72aeea103e9e4f63d2b"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:996cc95830de9bc22b183661d95559ec6b3cd900ad7bc9154c4cbf5be0c9b734"}, - {file = "rpds_py-0.8.10.tar.gz", hash = "sha256:13e643ce8ad502a0263397362fb887594b49cf84bf518d6038c16f235f2bcea4"}, -] - -[[package]] -name = "sentry-sdk" -version = "1.15.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.15.0.tar.gz", hash = "sha256:69ecbb2e1ff4db02a06c4f20f6f69cb5dfe3ebfbc06d023e40d77cf78e9c37e7"}, - {file = "sentry_sdk-1.15.0-py2.py3-none-any.whl", hash = "sha256:7ad4d37dd093f4a7cb5ad804c6efe9e8fab8873f7ffc06042dc3f3fd700a93ec"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "sepaxml" -version = "2.6.1" -description = "Python SEPA XML implementations" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sepaxml-2.6.1-py3-none-any.whl", hash = "sha256:f110e4a11322c1ea46f86550d703468b109343b4cece1d5b792d394f03d86110"}, - {file = "sepaxml-2.6.1.tar.gz", hash = "sha256:939c12236779e6a3d7221d3557921abd6e30b0bfd369e67815ea55bccaacd688"}, -] - -[package.dependencies] -text-unidecode = "*" -xmlschema = "*" - -[[package]] -name = "setuptools" -version = "68.0.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "slimit" -version = "0.8.1" -description = "SlimIt - JavaScript minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "slimit-0.8.1.zip", hash = "sha256:f433dcef899f166b207b67d91d3f7344659cb33b8259818f084167244e17720b"}, -] - -[package.dependencies] -ply = ">=3.4" - -[[package]] -name = "soupsieve" -version = "2.4.1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, -] - -[[package]] -name = "sqlparse" -version = "0.4.4" -description = "A non-validating SQL parser." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, - {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, -] - -[package.extras] -dev = ["build", "flake8"] -doc = ["sphinx"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "static3" -version = "0.7.0" -description = "A really simple WSGI way to serve static (or mixed) content." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "static3-0.7.0.tar.gz", hash = "sha256:674641c64bc75507af2eb20bef7e7e3593dca993dec6674be108fa15b42f47c8"}, -] - -[package.extras] -genshimagic = ["Genshi"] -kidmagic = ["kid"] - -[[package]] -name = "stripe" -version = "5.4.0" -description = "Python bindings for the Stripe API" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "stripe-5.4.0-py2.py3-none-any.whl", hash = "sha256:57c0da7e3b889b69ff1dbf23ac1ec5e00f665cfba069fdf0f328b83ddf4225df"}, - {file = "stripe-5.4.0.tar.gz", hash = "sha256:72bda7bf9be7528e1b97a5bbacb0716cdf6a0c9597b13fdbfa364cec3c130713"}, -] - -[package.dependencies] -requests = {version = ">=2.20", markers = "python_version >= \"3.0\""} - -[[package]] -name = "text-unidecode" -version = "1.3" -description = "The most basic Text::Unidecode port" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, - {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, -] - -[[package]] -name = "tlds" -version = "2023052200" -description = "Automatically updated list of valid TLDs taken directly from IANA" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "tlds-2023052200-py2.py3-none-any.whl", hash = "sha256:966e23eb6405ef83b1a3642a5181efd16f62ae0b2ec4a5621eea8060be242c9c"}, - {file = "tlds-2023052200.tar.gz", hash = "sha256:a3f27d4b13ec204fd21746ac8f1646e7c25a6061d2f42c3d7451d38e1e2a6cc3"}, -] - -[[package]] -name = "tqdm" -version = "4.65.0" -description = "Fast, Extensible Progress Meter" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, - {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "tzdata" -version = "2023.3" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, -] - -[[package]] -name = "ujson" -version = "5.8.0" -description = "Ultra fast JSON encoder and decoder for Python" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ujson-5.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4511560d75b15ecb367eef561554959b9d49b6ec3b8d5634212f9fed74a6df1"}, - {file = "ujson-5.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9399eaa5d1931a0ead49dce3ffacbea63f3177978588b956036bfe53cdf6af75"}, - {file = "ujson-5.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4e7bb7eba0e1963f8b768f9c458ecb193e5bf6977090182e2b4f4408f35ac76"}, - {file = "ujson-5.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40931d7c08c4ce99adc4b409ddb1bbb01635a950e81239c2382cfe24251b127a"}, - {file = "ujson-5.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d53039d39de65360e924b511c7ca1a67b0975c34c015dd468fca492b11caa8f7"}, - {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bdf04c6af3852161be9613e458a1fb67327910391de8ffedb8332e60800147a2"}, - {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a70f776bda2e5072a086c02792c7863ba5833d565189e09fabbd04c8b4c3abba"}, - {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f26629ac531d712f93192c233a74888bc8b8212558bd7d04c349125f10199fcf"}, - {file = "ujson-5.8.0-cp310-cp310-win32.whl", hash = "sha256:7ecc33b107ae88405aebdb8d82c13d6944be2331ebb04399134c03171509371a"}, - {file = "ujson-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:3b27a8da7a080add559a3b73ec9ebd52e82cc4419f7c6fb7266e62439a055ed0"}, - {file = "ujson-5.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:193349a998cd821483a25f5df30b44e8f495423840ee11b3b28df092ddfd0f7f"}, - {file = "ujson-5.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ddeabbc78b2aed531f167d1e70387b151900bc856d61e9325fcdfefb2a51ad8"}, - {file = "ujson-5.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ce24909a9c25062e60653073dd6d5e6ec9d6ad7ed6e0069450d5b673c854405"}, - {file = "ujson-5.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27a2a3c7620ebe43641e926a1062bc04e92dbe90d3501687957d71b4bdddaec4"}, - {file = "ujson-5.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b852bdf920fe9f84e2a2c210cc45f1b64f763b4f7d01468b33f7791698e455e"}, - {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:20768961a6a706170497129960762ded9c89fb1c10db2989c56956b162e2a8a3"}, - {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0147d41e9fb5cd174207c4a2895c5e24813204499fd0839951d4c8784a23bf5"}, - {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e3673053b036fd161ae7a5a33358ccae6793ee89fd499000204676baafd7b3aa"}, - {file = "ujson-5.8.0-cp311-cp311-win32.whl", hash = "sha256:a89cf3cd8bf33a37600431b7024a7ccf499db25f9f0b332947fbc79043aad879"}, - {file = "ujson-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3659deec9ab9eb19e8646932bfe6fe22730757c4addbe9d7d5544e879dc1b721"}, - {file = "ujson-5.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:102bf31c56f59538cccdfec45649780ae00657e86247c07edac434cb14d5388c"}, - {file = "ujson-5.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:299a312c3e85edee1178cb6453645217ba23b4e3186412677fa48e9a7f986de6"}, - {file = "ujson-5.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e385a7679b9088d7bc43a64811a7713cc7c33d032d020f757c54e7d41931ae"}, - {file = "ujson-5.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad24ec130855d4430a682c7a60ca0bc158f8253ec81feed4073801f6b6cb681b"}, - {file = "ujson-5.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16fde596d5e45bdf0d7de615346a102510ac8c405098e5595625015b0d4b5296"}, - {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6d230d870d1ce03df915e694dcfa3f4e8714369cce2346686dbe0bc8e3f135e7"}, - {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9571de0c53db5cbc265945e08f093f093af2c5a11e14772c72d8e37fceeedd08"}, - {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7cba16b26efe774c096a5e822e4f27097b7c81ed6fb5264a2b3f5fd8784bab30"}, - {file = "ujson-5.8.0-cp312-cp312-win32.whl", hash = "sha256:48c7d373ff22366eecfa36a52b9b55b0ee5bd44c2b50e16084aa88b9de038916"}, - {file = "ujson-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ac97b1e182d81cf395ded620528c59f4177eee024b4b39a50cdd7b720fdeec6"}, - {file = "ujson-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a64cc32bb4a436e5813b83f5aab0889927e5ea1788bf99b930fad853c5625cb"}, - {file = "ujson-5.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e54578fa8838ddc722539a752adfce9372474114f8c127bb316db5392d942f8b"}, - {file = "ujson-5.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9721cd112b5e4687cb4ade12a7b8af8b048d4991227ae8066d9c4b3a6642a582"}, - {file = "ujson-5.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d9707e5aacf63fb919f6237d6490c4e0244c7f8d3dc2a0f84d7dec5db7cb54c"}, - {file = "ujson-5.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0be81bae295f65a6896b0c9030b55a106fb2dec69ef877253a87bc7c9c5308f7"}, - {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae7f4725c344bf437e9b881019c558416fe84ad9c6b67426416c131ad577df67"}, - {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9ab282d67ef3097105552bf151438b551cc4bedb3f24d80fada830f2e132aeb9"}, - {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:94c7bd9880fa33fcf7f6d7f4cc032e2371adee3c5dba2922b918987141d1bf07"}, - {file = "ujson-5.8.0-cp38-cp38-win32.whl", hash = "sha256:bf5737dbcfe0fa0ac8fa599eceafae86b376492c8f1e4b84e3adf765f03fb564"}, - {file = "ujson-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:11da6bed916f9bfacf13f4fc6a9594abd62b2bb115acfb17a77b0f03bee4cfd5"}, - {file = "ujson-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:69b3104a2603bab510497ceabc186ba40fef38ec731c0ccaa662e01ff94a985c"}, - {file = "ujson-5.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9249fdefeb021e00b46025e77feed89cd91ffe9b3a49415239103fc1d5d9c29a"}, - {file = "ujson-5.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2873d196725a8193f56dde527b322c4bc79ed97cd60f1d087826ac3290cf9207"}, - {file = "ujson-5.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a4dafa9010c366589f55afb0fd67084acd8added1a51251008f9ff2c3e44042"}, - {file = "ujson-5.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a42baa647a50fa8bed53d4e242be61023bd37b93577f27f90ffe521ac9dc7a3"}, - {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f3554eaadffe416c6f543af442066afa6549edbc34fe6a7719818c3e72ebfe95"}, - {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fb87decf38cc82bcdea1d7511e73629e651bdec3a43ab40985167ab8449b769c"}, - {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:407d60eb942c318482bbfb1e66be093308bb11617d41c613e33b4ce5be789adc"}, - {file = "ujson-5.8.0-cp39-cp39-win32.whl", hash = "sha256:0fe1b7edaf560ca6ab023f81cbeaf9946a240876a993b8c5a21a1c539171d903"}, - {file = "ujson-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:3f9b63530a5392eb687baff3989d0fb5f45194ae5b1ca8276282fb647f8dcdb3"}, - {file = "ujson-5.8.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:efeddf950fb15a832376c0c01d8d7713479fbeceaed1eaecb2665aa62c305aec"}, - {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d8283ac5d03e65f488530c43d6610134309085b71db4f675e9cf5dff96a8282"}, - {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb0142f6f10f57598655340a3b2c70ed4646cbe674191da195eb0985a9813b83"}, - {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d459aca895eb17eb463b00441986b021b9312c6c8cc1d06880925c7f51009c"}, - {file = "ujson-5.8.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d524a8c15cfc863705991d70bbec998456a42c405c291d0f84a74ad7f35c5109"}, - {file = "ujson-5.8.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d6f84a7a175c75beecde53a624881ff618e9433045a69fcfb5e154b73cdaa377"}, - {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b748797131ac7b29826d1524db1cc366d2722ab7afacc2ce1287cdafccddbf1f"}, - {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e72ba76313d48a1a3a42e7dc9d1db32ea93fac782ad8dde6f8b13e35c229130"}, - {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f504117a39cb98abba4153bf0b46b4954cc5d62f6351a14660201500ba31fe7f"}, - {file = "ujson-5.8.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8c91b6f4bf23f274af9002b128d133b735141e867109487d17e344d38b87d94"}, - {file = "ujson-5.8.0.tar.gz", hash = "sha256:78e318def4ade898a461b3d92a79f9441e7e0e4d2ad5419abed4336d702c7425"}, -] - -[[package]] -name = "urllib3" -version = "2.0.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, - {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "vat-moss-forked" -version = "2020.3.20.0.11.0" -description = "Tools for VAT MOSS and Norway VAT on digital services." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "vat_moss_forked-2020.3.20.0.11.0-py3-none-any.whl", hash = "sha256:80f489da56836f5d3ad8e5b29621238a18888fa8669ebf9dd975c3666174471f"}, - {file = "vat_moss_forked-2020.3.20.0.11.0.tar.gz", hash = "sha256:d46836079687a20375cae0336946015060c71a30faa30642c8ea726daf638095"}, -] - -[[package]] -name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, - {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, -] - -[[package]] -name = "vobject" -version = "0.9.6.1" -description = "A full-featured Python package for parsing and creating iCalendar and vCard files" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "vobject-0.9.6.1.tar.gz", hash = "sha256:96512aec74b90abb71f6b53898dd7fe47300cc940104c4f79148f0671f790101"}, -] - -[package.dependencies] -python-dateutil = ">=2.4.0" - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, -] - -[[package]] -name = "webauthn" -version = "0.4.7" -description = "A WebAuthn Python module." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "webauthn-0.4.7-py2.py3-none-any.whl", hash = "sha256:238391b2e2cc60fb51a2cd2d2d6be149920b9af6184651353d9f95856617a9e7"}, - {file = "webauthn-0.4.7.tar.gz", hash = "sha256:8ad9072ff1d6169f3be30d4dc8733ea563dd266962397bc58b40f674a6af74ac"}, -] - -[package.dependencies] -cbor2 = ">=4.0.1" -cryptography = ">=2.3.1" -future = ">=0.17.1" -pyOpenSSL = ">=16.0.0" -six = ">=1.11.0" - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - -[[package]] -name = "xmlschema" -version = "2.3.1" -description = "An XML Schema validator and decoder" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "xmlschema-2.3.1-py3-none-any.whl", hash = "sha256:eac0e10957723689ff0691785da4ffee1e95df3a874e685a179047f7bf07f8fb"}, - {file = "xmlschema-2.3.1.tar.gz", hash = "sha256:2eb426c5710833a05610c22c8766713a1b87e9405e3eca0b7c658375bf7ec810"}, -] - -[package.dependencies] -elementpath = ">=4.1.2,<5.0.0" - -[package.extras] -codegen = ["elementpath (>=4.1.2,<5.0.0)", "jinja2"] -dev = ["Sphinx", "coverage", "elementpath (>=4.1.2,<5.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] -docs = ["Sphinx", "elementpath (>=4.1.2,<5.0.0)", "jinja2", "sphinx-rtd-theme"] - -[[package]] -name = "yarl" -version = "1.9.2" -description = "Yet another URL library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, - {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, - {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, - {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, - {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, - {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, - {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, - {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, - {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, - {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, - {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, - {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, - {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" - -[[package]] -name = "zeep" -version = "4.2.1" -description = "A Python SOAP client" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zeep-4.2.1-py3-none-any.whl", hash = "sha256:6754feb4c34a4b6d65fbc359252bf6654dcce3937bf1d95aae4402a60a8f5939"}, - {file = "zeep-4.2.1.tar.gz", hash = "sha256:72093acfdb1d8360ed400869b73fbf1882b95c4287f798084c42ee0c1ff0e425"}, -] - -[package.dependencies] -attrs = ">=17.2.0" -isodate = ">=0.5.4" -lxml = ">=4.6.0" -platformdirs = ">=1.4.0" -pytz = "*" -requests = ">=2.7.0" -requests-file = ">=1.5.1" -requests-toolbelt = ">=0.7.1" - -[package.extras] -async = ["httpx (>=0.15.0)"] -docs = ["sphinx (>=1.4.0)"] -test = ["coverage[toml] (==5.2.1)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)", "freezegun (==0.3.15)", "isort (==5.3.2)", "pretend (==1.0.9)", "pytest (==6.2.5)", "pytest-asyncio", "pytest-cov (==2.8.1)", "pytest-httpx", "requests-mock (>=0.7.0)"] -xmlsec = ["xmlsec (>=0.6.1)"] - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "d0fe6f5883fe243c1aaaff6a5358ab4289518d7413db33360152a02ad247d9b6" diff --git a/pkgs/pretix/pretix-banktool-requirements.patch b/pkgs/pretix/pretix-banktool-requirements.patch deleted file mode 100644 index 4dfe3bd..0000000 --- a/pkgs/pretix/pretix-banktool-requirements.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/setup.py b/setup.py -index 2eba88a..7041acd 100644 ---- a/setup.py -+++ b/setup.py -@@ -19,8 +19,8 @@ setup( - author_email='mail@raphaelmichel.de', - - install_requires=[ -- 'click==6.*', -- 'fints>=3.0.*', -+ 'click>=6,<8.2', -+ 'fints>=3,<4.1', - 'requests', -- 'mt-940>=4.12*', -+ 'mt-940>=4.12,<4.29', - ], --- -2.38.3 diff --git a/pkgs/pretix/pretix-banktool.nix b/pkgs/pretix/pretix-banktool.nix deleted file mode 100644 index 74d332d..0000000 --- a/pkgs/pretix/pretix-banktool.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ python3Packages, fetchFromGitHub }: -python3Packages.buildPythonApplication rec { - name = "pretix-banktool"; - version = "1.0.0"; - - src = fetchFromGitHub { - owner = "pretix"; - repo = "pretix-banktool"; - rev = "v${version}"; - sha256 = "vYHjotx1RujPV53Ei7bXAc3kL/3cwbWQB1T3sQ15MFA="; - }; - - patches = [ - ./pretix-banktool-requirements.patch - ]; - - propagatedBuildInputs = with python3Packages; [ - click - fints - mt-940 - requests - ]; -} diff --git a/pkgs/pretix/pretix-static.nix b/pkgs/pretix/pretix-static.nix deleted file mode 100644 index 2646bd1..0000000 --- a/pkgs/pretix/pretix-static.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ stdenvNoCC -, pretix -, buildNpmPackage -, makeWrapper -}: - -let - nodeEnv = buildNpmPackage rec { - name = "pretix-nodejs"; - src = "${pretix.passthru.pythonModule.pkgs.pretix}/lib/python3.10/site-packages/pretix/static/npm_dir"; - npmDepsHash = "sha256-wMzi48h4SFsGKi/s7FujJtsAtj8pRQX3nVo8WC0UqPY="; - dontNpmBuild = true; - installPhase = '' - mkdir -p $out - cp -r node_modules $out/ - mkdir -p $out/bin - ln -s $out/node_modules/rollup/dist/bin/rollup $out/bin/rollup - ''; - postFixup = '' - wrapProgram $out/bin/rollup --prefix NODE_PATH : $out - ''; - nativeBuildInputs = [ - makeWrapper - ]; - }; -in -stdenvNoCC.mkDerivation { - name = "pretix-static"; - src = ./.; - buildPhase = '' - mkdir $out - export PRETIX_STATIC_ROOT=$out - export DJANGO_SETTINGS_MODULE=pretix_wrapper.settings - ${pretix}/bin/pretix collectstatic --noinput - mkdir -p $PRETIX_STATIC_ROOT/node_prefix - ln -s ${nodeEnv}/node_modules $PRETIX_STATIC_ROOT/node_prefix/node_modules - echo ${nodeEnv}/bin/rollup - ${pretix}/bin/pretix compress - ''; - installPhase = '' - runHook preInstall - runHook postInstall - ''; - nativeBuildInputs = [ - nodeEnv - ]; -} - diff --git a/pkgs/pretix/pretix.nix b/pkgs/pretix/pretix.nix deleted file mode 100644 index a5b61f6..0000000 --- a/pkgs/pretix/pretix.nix +++ /dev/null @@ -1,55 +0,0 @@ -{ lib -, poetry2nix -, pkgs -, gettext -, tlds-alpha-by-domain ? ./tlds-alpha-by-domain.txt -}: - -let - tlds = pkgs.fetchurl { - url = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"; - sha256 = "0153py77ll759jacq41dp2z2ksr08pdcfic0rwjd6pr84dk89y9v"; - }; - pkgsRequiringSetuptools = [ - "dj-static" - "django-jquery-js" - "paypal-checkout-serversdk" - "python-u2flib-server" - "slimit" - "static3" - ]; -in -poetry2nix.mkPoetryApplication rec { - projectDir = ./.; - #python = pkgs.python310; - preferWheels = true; - overrides = poetry2nix.defaultPoetryOverrides.extend - ( - self: super: lib.attrsets.genAttrs pkgsRequiringSetuptools - ( - pythonPackage: - super."${pythonPackage}".overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ]; - } - ) - ) // { - tlds = super.tlds.overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ]; - } - ); - pretix = super.pretix.overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ - gettext - ]; - preFixup = '' - python -m pretix compilemessages - python -m pretix compilejsi18n - ''; - } - ); - } - ); -} diff --git a/pkgs/pretix/pretix_wrapper/__main__.py b/pkgs/pretix/pretix_wrapper/__main__.py deleted file mode 100644 index 57fdb08..0000000 --- a/pkgs/pretix/pretix_wrapper/__main__.py +++ /dev/null @@ -1,9 +0,0 @@ -import sys -import os - -module_name = "pretix" - - -def main(): - os.environ["PYTHONPATH"] = ":".join(sys.path) - os.execv(sys.executable, [sys.executable, "-m", module_name, *sys.argv[1:]]) diff --git a/pkgs/pretix/pretix_wrapper/settings.py b/pkgs/pretix/pretix_wrapper/settings.py deleted file mode 100644 index e0c5620..0000000 --- a/pkgs/pretix/pretix_wrapper/settings.py +++ /dev/null @@ -1,4 +0,0 @@ -import os -from pretix.settings import * - -STATIC_ROOT = os.getenv("PRETIX_STATIC_ROOT") diff --git a/pkgs/pretix/pyproject.toml b/pkgs/pretix/pyproject.toml deleted file mode 100644 index 5fdb13d..0000000 --- a/pkgs/pretix/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[tool.poetry] -name = "pretix_wrapper" -version = "1.0.0" -description = "" -authors = ["Jakob Lechner "] -license = "MIT" - -[tool.poetry.dependencies] -python = "^3.10" -pretix = "^2023.6.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -pretix = "pretix_wrapper.__main__:main" - -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" diff --git a/pkgs/tabbed-box-maker/default.nix b/pkgs/tabbed-box-maker/default.nix index d30e534..dee949e 100644 --- a/pkgs/tabbed-box-maker/default.nix +++ b/pkgs/tabbed-box-maker/default.nix @@ -14,7 +14,7 @@ stdenvNoCC.mkDerivation { sha256 = "8TNNVMSwbvcEwkvMHecHtGLEpiX3F0g0EGsgO1YKBGQ="; }; - dontBild = true; + dontBuild = true; installPhase = '' mkdir $out cp * $out diff --git a/pkgs/vesc-tool/firmware.nix b/pkgs/vesc-tool/firmware.nix deleted file mode 100644 index cb16051..0000000 --- a/pkgs/vesc-tool/firmware.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, gcc-arm-embedded-7 -, python311 -, git -}: - -stdenv.mkDerivation rec { - pname = "vesc-firmware"; - version = "master"; - - src = fetchFromGitHub { - owner = "vedderb"; - repo = "bldc"; - rev = "6.00"; - sha256 = "XeIS+3SOKeTkbsx/aCy0FRw/LVXBbLpAqz6upN5HJUY="; - #rev = "5b6cc075d3cfa62bd52a05fad0195fae114d914c"; - #sha256 = "SB/mQEYY2Bi2xl5qniKdShMQ0vdrVuLtiWWH4D1U0tw="; - fetchSubmodules = true; - }; - - nativeBuildInputs = [ - gcc-arm-embedded-7 - python311 - git - # bash - # libsForQt5.qmake - # libsForQt5.qtconnectivity - # libsForQt5.qtgamepad - # libsForQt5.qtlocation - # libsForQt5.qtquickcontrols2 - # libsForQt5.qtserialport - ]; - - buildCommand = '' - cp -r $src bldc - ( - cd bldc - chmod +w . - #make all_fw_package - make fw_410 - ) - cp -r bldc/build/* $out - ''; -} diff --git a/pkgs/vesc-tool/tool.nix b/pkgs/vesc-tool/tool.nix deleted file mode 100644 index 2a23fa3..0000000 --- a/pkgs/vesc-tool/tool.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, libsForQt5 -, vesc-firmware -, writeText -, qt5 -}: - -let - qresource = writeText "res_fw.qrc" - '' - - - 410.bin - - - ''; -in -stdenv.mkDerivation rec { - pname = "vesc_tool"; - version = "master"; - - src = fetchFromGitHub { - owner = "vedderb"; - repo = "vesc_tool"; - rev = "6d9d6d7f17c8756a272c267659849ea9c9404efa"; - sha256 = "EXXWAwX3yPirhe83wz5/vEectieHeUAkIla7ICxzMow="; - fetchSubmodules = true; - }; - - nativeBuildInputs = [ - libsForQt5.qmake - qt5.wrapQtAppsHook - ]; - - buildInputs = [ - vesc-firmware - ]; - - propagatedBuildInputs = with qt5; [ - qtconnectivity - qtgamepad - qtlocation - qtquickcontrols2 - qtserialport - ]; - - buildPhase = '' - tempdir="$(mktemp -d)" - cp -r "$src/." "$tempdir/" - chmod -R +w "$tempdir" - cd "$tempdir" - - mkdir -p res/firmwares/ - cp ${qresource} res/firmwares/res_fw.qrc - cp ${vesc-firmware}/*.bin res/firmwares - - qmake -config release "CONFIG += release_lin build_platinum" - make clean - make -j8 - rm -rf build/lin/obj - mkdir -p $out/bin - cp build/lin/* $out/bin - ''; - - postFixup = '' - wrapQtApp $out/bin/vesc_tool_6.02 - ''; -} diff --git a/pkgs/vim-fluid/default.nix b/pkgs/vim-fluid/default.nix new file mode 100644 index 0000000..35f232f --- /dev/null +++ b/pkgs/vim-fluid/default.nix @@ -0,0 +1,12 @@ +{ buildVimPlugin, fetchFromGitHub }: +buildVimPlugin { + pname = "vim-fluid"; + version = "0.0.1"; + src = fetchFromGitHub { + owner = "mipmip"; + repo = "vim-fluid"; + rev = "cedc4ad871941e8f7134d1d71f9434f1bc3d93d5"; + sha256 = "sha256-LiS2Dqw1K1Fu5VfHQnxIBDxDzEarmSAUUavQcwHRDsQ="; + }; + meta.homepage = "https://github.com/mipmip/vim-fluid"; +} diff --git a/pkgs/vim-typoscript/default.nix b/pkgs/vim-typoscript/default.nix new file mode 100644 index 0000000..ab85d79 --- /dev/null +++ b/pkgs/vim-typoscript/default.nix @@ -0,0 +1,13 @@ +{ buildVimPlugin, fetchFromGitHub }: +buildVimPlugin rec { + pname = "vim-typoscript"; + version = "2.0.0"; + src = fetchFromGitHub { + owner = "DanielSiepmann"; + repo = "mirror-vim.typoscript"; + rev = "v${version}"; + sha256 = "sha256-fCB+ikDmkfEP/W0pFYGrsZiH30vT0g3z6GZpRGk0Rhc="; + }; + meta.homepage = "https://git.daniel-siepmann.de/danielsiepmann/vim-syntax-typoscript"; +} + diff --git a/pkgs/vodafone-station-exporter/default.nix b/pkgs/vodafone-station-exporter/default.nix new file mode 100644 index 0000000..9a80e97 --- /dev/null +++ b/pkgs/vodafone-station-exporter/default.nix @@ -0,0 +1,12 @@ +{ buildGoApplication, fetchgit }: + +buildGoApplication { + pname = "vodafone-station-exporter"; + version = "0.0.1"; + src = fetchgit { + url = "https://git.jalr.de/jalr/vodafone-station-exporter"; + rev = "808564b940c3570e3b32ce60657bf83fda75ec3c"; + hash = "sha256-A3Behy8Q7bhYXoGUsZXzIAQd/dTXH4d4wd+FDYuD7tE="; + }; + modules = ./gomod2nix.toml; +} diff --git a/pkgs/vodafone-station-exporter/gomod2nix.toml b/pkgs/vodafone-station-exporter/gomod2nix.toml new file mode 100644 index 0000000..293cd7e --- /dev/null +++ b/pkgs/vodafone-station-exporter/gomod2nix.toml @@ -0,0 +1,42 @@ +schema = 3 + +[mod] + [mod."github.com/beorn7/perks"] + version = "v1.0.1" + hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.3.0" + hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=" + [mod."github.com/kr/text"] + version = "v0.2.0" + hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE=" + [mod."github.com/munnerz/goautoneg"] + version = "v0.0.0-20191010083416-a7dc8b61c822" + hash = "sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q=" + [mod."github.com/prometheus/client_golang"] + version = "v1.23.2" + hash = "sha256-3GD4fBFa1tJu8MS4TNP6r2re2eViUE+kWUaieIOQXCg=" + [mod."github.com/prometheus/client_model"] + version = "v0.6.2" + hash = "sha256-q6Fh6v8iNJN9ypD47LjWmx66YITa3FyRjZMRsuRTFeQ=" + [mod."github.com/prometheus/common"] + version = "v0.66.1" + hash = "sha256-bqHPaV9IV70itx63wqwgy2PtxMN0sn5ThVxDmiD7+Tk=" + [mod."github.com/prometheus/procfs"] + version = "v0.16.1" + hash = "sha256-OBCvKlLW2obct35p0L9Q+1ZrxZjpTmbgHMP2rng9hpo=" + [mod."go.yaml.in/yaml/v2"] + version = "v2.4.2" + hash = "sha256-oC8RWdf1zbMYCtmR0ATy/kCkhIwPR9UqFZSMOKLVF/A=" + [mod."golang.org/x/crypto"] + version = "v0.42.0" + hash = "sha256-qa6cGxZUhVnbkpVzfvLGQQsl/NCqNceJp9SIx5vkyiI=" + [mod."golang.org/x/exp"] + version = "v0.0.0-20250911091902-df9299821621" + hash = "sha256-cSDirFex900mrckzB3fe18hW2Vk4/y4xKvlUWq3yoDA=" + [mod."golang.org/x/sys"] + version = "v0.36.0" + hash = "sha256-9h4SHGnlJzmTENUp6226hC8fQ73QrQC3D85NNMxLuXg=" + [mod."google.golang.org/protobuf"] + version = "v1.36.8" + hash = "sha256-yZN8ZON0b5HjUNUSubHst7zbvnMsOzd81tDPYQRtPgM=" diff --git a/pkgs/wofi-bluetooth/wofi-bluetooth.nix b/pkgs/wofi-bluetooth/wofi-bluetooth.nix new file mode 100644 index 0000000..5f6e408 --- /dev/null +++ b/pkgs/wofi-bluetooth/wofi-bluetooth.nix @@ -0,0 +1,30 @@ +{ lib +, stdenv +, makeWrapper +, bluez +, wofi +, rofi-bluetooth +}: + +stdenv.mkDerivation rec { + pname = "wofi-bluetooth"; + inherit (rofi-bluetooth) version; + inherit (rofi-bluetooth) src; + patches = [ + ./wofi-bluetooth.patch + ]; + buildInputs = [ wofi ]; + nativeBuildInputs = [ makeWrapper ]; + installPhase = '' + runHook preInstall + + install -D --target-directory=$out/bin/ ./rofi-bluetooth + + mv $out/bin/rofi-bluetooth $out/bin/wofi-bluetooth + + wrapProgram $out/bin/wofi-bluetooth \ + --prefix PATH ":" ${lib.makeBinPath [ bluez wofi ] } + + runHook postInstall + ''; +} diff --git a/pkgs/wofi-bluetooth/wofi-bluetooth.patch b/pkgs/wofi-bluetooth/wofi-bluetooth.patch new file mode 100644 index 0000000..3150f7a --- /dev/null +++ b/pkgs/wofi-bluetooth/wofi-bluetooth.patch @@ -0,0 +1,68 @@ +--- a/rofi-bluetooth ++++ b/rofi-bluetooth +@@ -7,14 +7,14 @@ + # + # Author: Nick Clyde (clydedroid) + # +-# A script that generates a rofi menu that uses bluetoothctl to ++# A script that generates a wofi menu that uses bluetoothctl to + # connect to bluetooth devices and display status info. + # + # Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu) + # Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl) + # + # Depends on: +-# Arch repositories: rofi, bluez-utils (contains bluetoothctl) ++# Arch repositories: wofi, bluez-utils (contains bluetoothctl) + + # Constants + divider="---------" +@@ -231,8 +231,8 @@ + trusted=$(device_trusted "$mac") + options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit" + +- # Open rofi menu, read chosen option +- chosen="$(echo -e "$options" | $rofi_command "$device_name")" ++ # Open wofi menu, read chosen option ++ chosen="$(echo -e "$options" | $wofi_command "$device_name")" + + # Match chosen option to command + case "$chosen" in +@@ -254,7 +254,7 @@ + esac + } + +-# Opens a rofi menu with current bluetooth status and options to connect ++# Opens a wofi menu with current bluetooth status and options to connect + show_menu() { + # Get menu options + if power_on; then +@@ -269,15 +269,16 @@ + pairable=$(pairable_on) + discoverable=$(discoverable_on) + +- # Options passed to rofi ++ # Options passed to wofi + options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" + else + power="Power: off" + options="$power\nExit" + fi + +- # Open rofi menu, read chosen option +- chosen="$(echo -e "$options" | $rofi_command "Bluetooth")" ++ lines="$(echo -e "$options" | wc -l)" ++ # Open wofi menu, read chosen option ++ chosen="$(echo -e "$options" | $wofi_command "Bluetooth" -L "$lines")" + + # Match chosen option to command + case "$chosen" in +@@ -305,7 +306,7 @@ + } + + # Rofi command to pipe into, can add any options here +-rofi_command="rofi -dmenu $* -p" ++wofi_command="wofi --color=$HOME/.config/wofi/color -d -i -p " + + case "$1" in + --status) diff --git a/users/README.md b/users/README.md new file mode 100644 index 0000000..50675ba --- /dev/null +++ b/users/README.md @@ -0,0 +1,7 @@ +# Home Manager +The user configuration is managed by [Home Manager](https://github.com/nix-community/home-manager) + +For a systematic overview of Home Manager and its available options, please see +- the [Home Manager manual](https://nix-community.github.io/home-manager/index.html) and +- the [Home Manager configuration options](https://nix-community.github.io/home-manager/options.html). + diff --git a/users/jalr/default.nix b/users/jalr/default.nix new file mode 100644 index 0000000..ac85cd2 --- /dev/null +++ b/users/jalr/default.nix @@ -0,0 +1,206 @@ +{ config, pkgs, ... }: + +let + sshKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3l+Yixrsjhze20CSjvUK4Qj/BNqbTNitgk20vuzPej cardno:25_750_479" + ]; +in +{ + users.users.jalr = { + isNormalUser = true; + extraGroups = [ + "adbusers" + "audio" + "dialout" + "docker" + "libvirtd" + "lp" + "networkmanager" + "plugdev" + "scanner" + "video" + "wheel" + "wireshark" + ]; # Enable ‘sudo’ for the user. + shell = pkgs.fish; + openssh.authorizedKeys.keys = sshKeys; + }; + + users.users.root.openssh.authorizedKeys.keys = sshKeys; + + home-manager = { + useUserPackages = true; + useGlobalPkgs = true; + backupFileExtension = "hm.bak"; + users.jalr = { pkgs, ... }: { + imports = [ ./modules ]; + config = { + home.stateVersion = config.system.stateVersion; + + home.packages = with pkgs; [ + cutecom + ghostscript + newsboat + pdftk + platformio + ptouch-print + qrencode + sshfs + tmate + + # common + asciinema + bat + envsubst + gnupg + nmap + psutils + pwgen + ]; + + xdg.mimeApps = { + enable = true; + defaultApplications = { + "application/pdf" = "org.gnome.Evince.desktop"; + + "image/svg+xml" = "org.inkscape.Inkscape.desktop"; + + "x-scheme-handler/http" = "firefox-esr.desktop"; + "x-scheme-handler/https" = "firefox-esr.desktop"; + + "x-scheme-handler/mailto" = "thunderbird.desktop"; + }; + }; + + accounts.email.accounts = { + "jalr" = { + primary = true; + userName = "jalr@jalr.de"; + address = "jalr@jalr.de"; + realName = "Jakob Lechner"; + imap = { + host = "hha.jalr.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "hha.jalr.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Digitaler Dienst" = { + userName = "j.lechner@digitaler-dienst.gmbh"; + address = "j.lechner@digitaler-dienst.gmbh"; + realName = "Jakob Lechner"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Digitaler Dienst info" = { + userName = "info@digitaler-dienst.gmbh"; + address = "info@digitaler-dienst.gmbh"; + realName = "Digitaler Dienst"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 587; + tls = { + enable = true; + useStartTls = true; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "FabLab NEA" = { + userName = "kontakt@fablab-nea.de"; + address = "kontakt@fablab-nea.de"; + realName = "FabLab NEA"; + imap = { + host = "hha.jalr.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "hha.jalr.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Weinturm Open Air - IT" = { + userName = "it@weinturm-open-air.de"; + address = "it@weinturm-open-air.de"; + realName = "Weinturm Open Air IT"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 587; + tls = { + enable = true; + useStartTls = true; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + }; + }; + }; + }; +} diff --git a/home-manager/modules/openscad.nix b/users/jalr/modules/3d-modeling.nix similarity index 100% rename from home-manager/modules/openscad.nix rename to users/jalr/modules/3d-modeling.nix diff --git a/home-manager/modules/communication/telegram-desktop.nix b/users/jalr/modules/3d-printing.nix similarity index 86% rename from home-manager/modules/communication/telegram-desktop.nix rename to users/jalr/modules/3d-printing.nix index d8876cc..5107e9c 100644 --- a/home-manager/modules/communication/telegram-desktop.nix +++ b/users/jalr/modules/3d-printing.nix @@ -2,6 +2,6 @@ lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - tdesktop + prusa-slicer ]; } diff --git a/users/jalr/modules/ardour.nix b/users/jalr/modules/ardour.nix new file mode 100644 index 0000000..4d3d5ac --- /dev/null +++ b/users/jalr/modules/ardour.nix @@ -0,0 +1,8 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + ardour + x42-plugins + ]; + home.sessionVariables.LV2_PATH = "${pkgs.x42-plugins}/lib/lv2/"; +} diff --git a/home-manager/modules/aws.nix b/users/jalr/modules/aws.nix similarity index 85% rename from home-manager/modules/aws.nix rename to users/jalr/modules/aws.nix index 85bbd23..2e85f92 100644 --- a/home-manager/modules/aws.nix +++ b/users/jalr/modules/aws.nix @@ -1,7 +1,7 @@ -{ nixosConfig, lib, pkgs, config, ... }: +{ nixosConfig, lib, config, ... }: let - xdg = config.xdg; + inherit (config) xdg; in { config = lib.mkIf nixosConfig.jalr.aws.enable { @@ -17,7 +17,7 @@ in xdg.configFile."aws/config".text = lib.generators.toINI { } ( lib.mapAttrs' (name: value: - lib.attrsets.nameValuePair ("profile ${name}") (value) + lib.attrsets.nameValuePair "profile ${name}" value ) nixosConfig.jalr.aws.accounts // diff --git a/users/jalr/modules/cli/default.nix b/users/jalr/modules/cli/default.nix new file mode 100644 index 0000000..5ab60f5 --- /dev/null +++ b/users/jalr/modules/cli/default.nix @@ -0,0 +1,28 @@ +{ nixosConfig, pkgs, ... }: +{ + imports = [ + ./htop.nix + ]; + + config = { + home.packages = with pkgs; [ + cached-nix-shell + fd + file + inetutils + jq + lsof + ncdu + ripgrep + tio + unzip + ] ++ (if ! nixosConfig.jalr.workstation.enable then [ ] else [ + dnsutils + screen + speedtest-cli + usbutils + wget + yt-dlp + ]); + }; +} diff --git a/users/jalr/modules/cli/htop.nix b/users/jalr/modules/cli/htop.nix new file mode 100644 index 0000000..cc0b753 --- /dev/null +++ b/users/jalr/modules/cli/htop.nix @@ -0,0 +1,24 @@ +{ nixosConfig +, config +, lib +, ... +}: + +{ + programs.htop = { + enable = true; + settings = { + color_scheme = 6; + } // (with config.lib.htop; leftMeters ([ + (bar "LeftCPUs") + (bar "Memory") + ] ++ lib.lists.optional nixosConfig.zramSwap.enable (bar "Zram") ++ lib.lists.optional (nixosConfig.swapDevices != [ ]) (bar "Swap") ++ [ + (bar "DiskIO") + ])) // (with config.lib.htop; rightMeters [ + (bar "RightCPUs") + (text "Tasks") + (text "LoadAverage") + (text "NetworkIO") + ]); + }; +} diff --git a/users/jalr/modules/communication/default.nix b/users/jalr/modules/communication/default.nix new file mode 100644 index 0000000..f461097 --- /dev/null +++ b/users/jalr/modules/communication/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./ferdium.nix + ./mumble.nix + ]; +} diff --git a/users/jalr/modules/communication/ferdium.nix b/users/jalr/modules/communication/ferdium.nix new file mode 100644 index 0000000..1d0b4d5 --- /dev/null +++ b/users/jalr/modules/communication/ferdium.nix @@ -0,0 +1,14 @@ +{ nixosConfig, lib, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + xdg.desktopEntries.Ferdium = { + name = "Ferdium"; + exec = "flatpak run --branch=stable --arch=x86_64 --command=ferdium --file-forwarding org.ferdium.Ferdium @@u %U @@"; + terminal = false; + type = "Application"; + icon = "org.ferdium.Ferdium"; + comment = "Desktop app bringing all your messaging services into one installable"; + mimeType = [ "x-scheme-handler/ferdium" ]; + categories = [ "Network" "InstantMessaging" ]; + }; +} diff --git a/home-manager/modules/communication/mumble.nix b/users/jalr/modules/communication/mumble.nix similarity index 100% rename from home-manager/modules/communication/mumble.nix rename to users/jalr/modules/communication/mumble.nix diff --git a/users/jalr/modules/dconf.nix b/users/jalr/modules/dconf.nix new file mode 100644 index 0000000..eed759c --- /dev/null +++ b/users/jalr/modules/dconf.nix @@ -0,0 +1,15 @@ +{ lib, nixosConfig, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + dconf.settings = { + "org/gnome/desktop/input-sources" = { + xkb-options = [ "grp:win_space_toggle" ]; + show-all-sources = true; + sources = [ + (lib.hm.gvariant.mkTuple [ "xkb" "de" ]) + (lib.hm.gvariant.mkTuple [ "xkb" "de+neo" ]) + (lib.hm.gvariant.mkTuple [ "xkb" "us" ]) + ]; + }; + }; +} diff --git a/users/jalr/modules/ddev.nix b/users/jalr/modules/ddev.nix new file mode 100644 index 0000000..2e154d2 --- /dev/null +++ b/users/jalr/modules/ddev.nix @@ -0,0 +1,6 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ + pkgs.master.ddev + ]; +} diff --git a/users/jalr/modules/default.nix b/users/jalr/modules/default.nix new file mode 100644 index 0000000..f97916b --- /dev/null +++ b/users/jalr/modules/default.nix @@ -0,0 +1,51 @@ +{ + imports = [ + ./3d-modeling.nix + ./3d-printing.nix + ./ardour.nix + ./aws.nix + ./cli + ./communication + ./dconf.nix + ./ddev.nix + ./direnv.nix + ./do-not-disturb + ./dynamic-colors.nix + ./firefox + ./fish.nix + ./fpv.nix + ./freetube.nix + ./git.nix + ./gnuradio.nix + ./graphics + ./gui.nix + ./jameica.nix + ./kicad.nix + ./lsd + ./mixxc + ./mpv.nix + ./mute-indicator.nix + ./mycli + ./neovim + ./nix-index.nix + ./obs-studio + ./ots.nix + ./pace.nix + ./pass.nix + ./pomodoro.nix + ./python.nix + ./remarkable + ./roomeqwizard.nix + ./snapclient.nix + ./sound + ./sway + ./thunar.nix + ./thunderbird.nix + ./tmux.nix + ./tor-browser.nix + ./trilium.nix + ./vdirsyncer.nix + ./vesc-tool.nix + ./wezterm.nix + ]; +} diff --git a/home-manager/modules/direnv.nix b/users/jalr/modules/direnv.nix similarity index 100% rename from home-manager/modules/direnv.nix rename to users/jalr/modules/direnv.nix diff --git a/users/jalr/modules/do-not-disturb/android-set-dnd.py b/users/jalr/modules/do-not-disturb/android-set-dnd.py new file mode 100755 index 0000000..13a7eba --- /dev/null +++ b/users/jalr/modules/do-not-disturb/android-set-dnd.py @@ -0,0 +1,28 @@ +import os +import sys +from urllib import request + + +def read_url(): + try: + with open(os.getenv("NTFY_URL_FILE")) as f: + return f.read() + except FileNotFoundError: + return None + + +def set_android_dnd(active: bool): + url = read_url() + if url is not None: + request.urlopen( + request.Request( + read_url(), + method="POST", + data=("DND on" if active else "DND off").encode(), + ) + ) + + +if __name__ == "__main__": + _, state = sys.argv + set_android_dnd(state == "on") diff --git a/users/jalr/modules/do-not-disturb/default.nix b/users/jalr/modules/do-not-disturb/default.nix new file mode 100644 index 0000000..29b0c2f --- /dev/null +++ b/users/jalr/modules/do-not-disturb/default.nix @@ -0,0 +1,29 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + androidSetDnd = pkgs.writeScript "android-set-dnd" '' + #!${pkgs.python3}/bin/python3 + ${builtins.readFile ./android-set-dnd.py} + ''; + do-not-disturb = pkgs.writeShellScriptBin "dnd" '' + export PATH=${pkgs.lib.makeBinPath [pkgs.mako]} + + if [[ "$1" != off && "$1" != on ]]; then + echo "USAGE: $0 [on|off]" >&2 + exit 1 + fi + + export NTFY_URL_FILE=/run/secrets/ntfy_shiftphone + ${androidSetDnd} $1 + + if [[ $1 == on ]]; then + makoctl mode -a dnd > /dev/null + else + makoctl mode -r dnd > /dev/null + fi + ''; + +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ do-not-disturb ]; +} diff --git a/users/jalr/modules/dynamic-colors.nix b/users/jalr/modules/dynamic-colors.nix new file mode 100644 index 0000000..c97e9ed --- /dev/null +++ b/users/jalr/modules/dynamic-colors.nix @@ -0,0 +1,141 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + loadSwayTheme = pkgs.writeShellScript "load-sway-theme" '' + while IFS= read -r line; do + ${pkgs.sway}/bin/swaymsg "$line" + done < "$1" + ''; + applicationConfig = [ + { + dir = "~/.config/wofi"; + light = "color-light"; + dark = "color-dark"; + target = "color"; + } + { + dir = "~/.config/sway"; + light = "light-theme"; + dark = "dark-theme"; + target = "theme"; + exec = [ loadSwayTheme "theme" ]; + } + { + dir = "~/.config/waybar"; + light = "theme-light.css"; + dark = "theme-dark.css"; + target = "theme.css"; + exec = [ "${pkgs.systemd}/bin/systemctl" "--user" "restart" "waybar.service" ]; + } + { + dir = "~/.config/mycli"; + light = "theme-light.ini"; + dark = "theme-dark.ini"; + target = "myclirc"; + } + { + exec = + if nixosConfig.jalr.gui.enable + then [ "/usr/bin/env" "gsettings" "set" "org.gnome.desktop.interface" "color-scheme" "prefer-%scheme%" ] + else null; + } + { + exec = + if nixosConfig.jalr.gui.enable + then [ "/usr/bin/env" "gsettings" "set" "org.gnome.desktop.interface" "gtk-theme" "Adwaita-%scheme%" ] + else null; + } + { + dir = "~/.config/lsd"; + light = "colors-light.yaml"; + dark = "colors-dark.yaml"; + target = "colors.yaml"; + } + ]; + dynamic-colors = pkgs.writers.writePython3Bin "dynamic-colors" { } '' + import json + import os + import pathlib + import subprocess + import sys + + + def main(): + DEFAULT_SCHEME = 'light' + CONFIG_FILE = "~/.config/dynamic-colors/config.json" + with open(pathlib.Path(CONFIG_FILE).expanduser(), "r") as fh: + config = json.load(fh) + + command, = sys.argv[1:] + + scheme = None + if command in ('light', 'dark'): + scheme = command + elif command == 'install': + pass + else: + raise NotImplementedError + + for entry in config: + directory = ( + pathlib.Path(entry['dir']).expanduser() if 'dir' in entry else None + ) + if all(key in entry for key in ( + 'target', + 'light', + 'dark' + )): + target = directory.joinpath(entry['target']) + + if scheme is None: + if target.exists(): + continue + scheme = DEFAULT_SCHEME + else: + if target.exists() and target.is_symlink: + os.remove(target) + + src = { + 'light': entry['light'], + 'dark': entry['dark'], + }[scheme] + + try: + os.symlink(src, target) + except FileNotFoundError: + pass + + if entry.get('exec') is not None: + command, *args = entry["exec"] + args = [ + arg.replace( + '%scheme%', scheme + if scheme is not None + else DEFAULT_SCHEME + ) + for arg in args + ] + print(command, *args) + try: + subprocess.run( + (command, *args), + cwd=directory + ) + except FileNotFoundError: + pass + + + if __name__ == '__main__': + main() + ''; +in +{ + xdg.configFile."dynamic-colors/config.json" = { + text = lib.generators.toJSON { } applicationConfig; + onChange = "${dynamic-colors}/bin/dynamic-colors install"; + }; + + home.packages = [ + dynamic-colors + ]; +} diff --git a/users/jalr/modules/firefox/default.nix b/users/jalr/modules/firefox/default.nix new file mode 100644 index 0000000..62b3eb5 --- /dev/null +++ b/users/jalr/modules/firefox/default.nix @@ -0,0 +1,385 @@ +{ nixosConfig, pkgs, ... }: +{ + programs.firefox = { + inherit (nixosConfig.jalr.gui) enable; + package = pkgs.firefox-esr.override { + nativeMessagingHosts = [ + pkgs.browserpass + ]; + }; + policies = { + AllowedDomainsForApps = ""; + CaptivePortal = false; + DNSOverHTTPS.Enabled = false; + DisableAppUpdate = true; + DisableFeedbackCommands = true; + DisableFirefoxAccounts = true; + DisableFirefoxScreenshots = true; + DisableFirefoxStudies = true; + DisablePocket = true; + DisableTelemetry = true; + DisplayBookmarksToolbar = "newtab"; + DisplayMenuBar = "never"; + EncryptedMediaExtensions = { Enabled = false; Locked = true; }; + NoDefaultBookmarks = true; + OfferToSaveLogins = false; + StartDownloadsInTempDirectory = true; + UserMessaging = { + WhatsNew = false; + ExtensionRecommendations = false; + FeatureRecommendations = false; + UrlbarInterventions = false; + SkipOnboarding = true; + MoreFromMozilla = false; + Locked = false; + }; + Permissions = { + Camera = { + /* + Allow = ["https://example.org" "https://example.org:1234"]; + Block = ["https://example.edu"]; + BlockNewRequests = true | false; + Locked = true | false; + */ + }; + Microphone = { }; + Location = { }; + Notifications = { }; + Autoplay = { }; + }; + PopupBlocking = { + /* Allow = ["http://example.org/" "http://example.edu/"]; */ + Default = false; + Locked = false; + }; + Bookmarks = ( + builtins.map + (b: b // { + Folder = "Nix"; + Placement = "toolbar"; + }) [ + { + Title = "NixOS Manual"; + URL = "https://nixos.org/manual/nixos/stable/"; + } + { + Title = "Nix manual"; + URL = "https://nix.dev/manual/nix/2.18/stable"; + } + { + Title = "Nixpkgs manual"; + URL = "https://nixos.org/manual/nixpkgs/stable/"; + } + { + Title = "Noogle"; + URL = "https://noogle.dev/"; + } + { + Title = "Home Manager Configuration Options"; + URL = "https://nix-community.github.io/home-manager/options.xhtml"; + } + { + Title = "Home Manager Option Search"; + URL = "https://mipmip.github.io/home-manager-option-search/"; + } + { + Title = "NixOS Status"; + URL = "https://status.nixos.org/"; + } + { + Title = "krops"; + URL = "https://cgit.krebsco.de/krops/about/"; + } + { + Title = "Awesome Nix"; + URL = "https://github.com/nix-community/awesome-nix"; + } + ] + ) ++ ( + builtins.map + (b: b // { + Folder = "Digitaler Dienst"; + Placement = "toolbar"; + }) [ + { + Title = "GitLab"; + URL = "https://gitlab.digitaler-dienst.net/"; + } + { + Title = "Moco"; + URL = "https://digitaler-dienst.mocoapp.com/activities"; + } + { + Title = "Leantime"; + URL = "https://todo.digitaler-dienst.gmbh/"; + } + { + Title = "Nextcloud"; + URL = "https://nx52865.your-storageshare.de/"; + } + { + Title = "FreeScout"; + URL = "https://tickets.digitaler-dienst.gmbh/"; + } + { + Title = "Personio"; + URL = "https://laemmermann.personio.de/"; + } + ] + ) ++ [ + { + Title = "Fefes Blog"; + URL = "https://blog.fefe.de"; + Placement = "toolbar"; + #Placement = "menu"; + #Favicon = "https://example.com/favicon.ico"; + } + ]; + /* + ManagedBookmarks = [ + { + toplevel_name = "My managed bookmarks folder"; + } + { + url = "example.com"; + name = "Example"; + } + { + name = "Mozilla links"; + children = [ + { + url = "https://mozilla.org"; + name = "Mozilla.org"; + } + { + url = "https://support.mozilla.org/"; + name = "SUMO"; + } + ]; + } + ]; + */ + SearchEngines = { + Default = "DuckDuckGo"; + Remove = [ + "Google" + "Wikipedia (en)" + ]; + Add = [ + { + Name = "Startpage"; + URLTemplate = "https://www.startpage.com/sp/search"; + Method = "POST"; + PostData = "qadf=none&query={searchTerms}"; + IconURL = "https://www.startpage.com/sp/cdn/favicons/mobile/android-icon-192x192.png"; + Alias = "sp"; + } + { + Name = "DuckDuckGo"; + URLTemplate = "https://duckduckgo.com/?q={searchTerms}"; + Method = "GET"; + IconURL = "https://duckduckgo.com/favicon.ico"; + Alias = "ddg"; + } + + # Wikipedia + { + Name = "Wikipedia en"; + URLTemplate = "https://en.wikipedia.org/wiki/Special:Search?search={searchTerms}"; + Method = "GET"; + IconURL = "https://en.wikipedia.org/static/images/icons/wikipedia.png"; + Alias = "wen"; + } + { + Name = "Wikipedia de"; + URLTemplate = "https://de.wikipedia.org/w/index.php?search={searchTerms}"; + Method = "GET"; + IconURL = "https://www.wikipedia.de/img/wikipedia.png"; + Alias = "wde"; + } + { + Name = "Nix Packages"; + URLTemplate = "https://search.nixos.org/packages?query={searchTerms}"; + Method = "GET"; + IconURL = "https://nixos.org/favicon.png"; + Alias = "pkg"; + } + { + Name = "NixOS Options"; + URLTemplate = "https://search.nixos.org/options?query={searchTerms}"; + Method = "GET"; + IconURL = "https://nixos.org/favicon.png"; + Alias = "opt"; + } + { + Name = "Docker images"; + URLTemplate = "https://hub.docker.com/search/?q={searchTerms}"; + Method = "GET"; + IconURL = "https://hub.docker.com/favicon.ico"; + Alias = "docker"; + } + { + Name = "GitHub"; + URLTemplate = "https://github.com/search?q={searchTerms}"; + Method = "GET"; + IconURL = "https://github.githubassets.com/favicons/favicon.svg"; + Alias = "gh"; + } + + # Shopping + { + Name = "Amazon de"; + URLTemplate = "https://www.amazon.de/s?k={searchTerms}"; + Method = "GET"; + IconURL = "https://www.amazon.de/favicon.ico"; + Alias = "amde"; + } + { + Name = "Ebay de"; + URLTemplate = "https://www.ebay.de/sch/i.html?_nkw={searchTerms}"; + Method = "GET"; + IconURL = "https://pages.ebay.com/favicon.ico"; + Alias = "ebde"; + } + + # Dictionary + { + Name = "dict.cc"; + URLTemplate = "https://www.dict.cc/?s={searchTerms}"; + Method = "GET"; + IconURL = "https://www4.dict.cc/img/favicons/favicon4.png"; + Alias = "dcc"; + } + { + Name = "Duden"; + URLTemplate = "https://www.duden.de/suchen/dudenonline/{searchTerms}"; + Method = "GET"; + IconURL = "https://www.duden.de/sites/default/res/apple-touch-icon/180x180.png"; + Alias = "duden"; + } + + # Map + { + Name = "OpenStreetMap"; + URLTemplate = "https://www.openstreetmap.org/search?query={searchTerms}"; + Method = "GET"; + IconURL = "https://www.openstreetmap.org/assets/favicon-194x194-79d3fb0152c735866e64b1d7535d504483cd13c2fad0131a6142bd9629d30de2.png"; + Alias = "osm"; + } + ]; + }; + }; + profiles.default = { + id = 0; + isDefault = true; + extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ + browserpass + darkreader + sponsorblock + (tree-style-tab.override { + version = "4.1.6"; + url = "https://addons.mozilla.org/firefox/downloads/file/4488104/tree_style_tab-4.1.6.xpi"; + sha256 = "sha256-X0HC6jzytjBsM+8HmbK48DUihtdN9oCsqLUJqp29csQ="; + }) + ublock-origin + umatrix + violentmonkey + xdebug-helper-for-firefox + youtube-shorts-block + ]; + settings = { + #"browser.startup.homepage" = "https://nixos.org"; + #"browser.search.region" = "GB"; + #"browser.search.isUS" = false; + #"distribution.searchplugins.defaultLocale" = "en-GB"; + #"general.useragent.locale" = "en-GB"; + #"browser.bookmarks.showMobileBookmarks" = true; + "app.normandy.enabled" = false; + "app.shield.optoutstudies.enabled" = false; + "app.update.auto" = false; + "browser.bookmarks.addedImportButton" = false; + "browser.ctrlTab.sortByRecentlyUsed" = true; + "browser.fixup.alternate.enabled" = false; + "browser.formfill.enable" = false; + "browser.link.open_newwindow.restriction" = 0; + "browser.newtabpage.enabled" = false; + "browser.ping-centre.telemetry" = false; + "browser.safebrowsing.downloads.enabled" = false; + "browser.safebrowsing.downloads.remote.block_dangerous" = false; + "browser.safebrowsing.downloads.remote.block_dangerous_host" = false; + "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false; + "browser.safebrowsing.downloads.remote.block_uncommon" = false; + "browser.safebrowsing.downloads.remote.enabled" = false; + "browser.safebrowsing.downloads.remote.url" = ""; + "browser.safebrowsing.malware.enabled" = false; + "browser.safebrowsing.phishing.enabled" = false; + "browser.safebrowsing.provider.google.advisoryURL" = ""; + "browser.safebrowsing.provider.google.gethashURL" = ""; + "browser.safebrowsing.provider.google.lists" = ""; + "browser.safebrowsing.provider.google.reportMalwareMistakeURL" = ""; + "browser.safebrowsing.provider.google.reportPhishMistakeURL" = ""; + "browser.safebrowsing.provider.google.reportURL" = ""; + "browser.safebrowsing.provider.google.updateURL" = ""; + "browser.safebrowsing.provider.google4.advisoryURL" = ""; + "browser.safebrowsing.provider.google4.dataSharingURL" = ""; + "browser.safebrowsing.provider.google4.gethashURL" = ""; + "browser.safebrowsing.provider.google4.lists" = ""; + "browser.safebrowsing.provider.google4.reportMalwareMistakeURL" = ""; + "browser.safebrowsing.provider.google4.reportPhishMistakeURL" = ""; + "browser.safebrowsing.provider.google4.reportURL" = ""; + "browser.safebrowsing.provider.google4.updateURL" = ""; + "browser.safebrowsing.provider.mozilla.gethashURL" = ""; + "browser.safebrowsing.provider.mozilla.lists" = ""; + "browser.safebrowsing.provider.mozilla.updateURL" = ""; + "browser.search.suggest.enabled" = false; + "browser.search.widget.inNavBar" = true; + "browser.startup.page" = 0; + "extensions.pocket.enabled" = false; + "extensions.update.enabled" = false; + "identity.fxaccounts.enabled" = false; + "keyword.enabled" = false; + "network.captive-portal-service.enabled" = false; + "network.predictor.enabled" = false; + "privacy.donottrackheader.enabled" = true; + "startup.homepage_welcome_url" = "about:blank"; + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + "toolkit.telemetry.archive.enabled" = false; + "toolkit.telemetry.bhrPing.enabled" = false; + "toolkit.telemetry.firstShutdownPing.enabled" = false; + "toolkit.telemetry.newProfilePing.enabled" = false; + "toolkit.telemetry.server" = "http://127.0.0.1:4711"; + "toolkit.telemetry.server_owner" = ""; + "toolkit.telemetry.shutdownPingSender.enabled" = false; + "toolkit.telemetry.updatePing.enabled" = false; + "urlclassifier.downloadAllowTable" = ""; + "urlclassifier.downloadBlockTable" = ""; + "urlclassifier.malwareTable" = ""; + "urlclassifier.phishTable" = ""; + "datareporting.healthreport.uploadEnabled" = ""; + "app.normandy.api_url" = ""; + "breakpad.reportURL" = ""; + "browser.region.network.url" = ""; + "browser.search.geoSpecificDefaults.url" = ""; + "browser.shell.checkDefaultBrowser" = false; + + "privacy.userContext.enabled" = true; + "privacy.userContext.ui.enabled" = true; + "network.dnsCacheExpiration" = 0; + + # disable disk cache to reduce ssd writes + "browser.cache.disk.enable" = false; + "browser.cache.memory.enable" = true; + "browser.cache.memory.capacity" = -1; + }; + + userChrome = builtins.readFile ./userChrome.css; + }; + }; + + programs.browserpass = { + enable = true; + browsers = [ "firefox" ]; + }; + + xdg.configFile."treestyletab.css".source = ./treestyletab.css; +} diff --git a/users/jalr/modules/firefox/treestyletab.css b/users/jalr/modules/firefox/treestyletab.css new file mode 100644 index 0000000..b5b430a --- /dev/null +++ b/users/jalr/modules/firefox/treestyletab.css @@ -0,0 +1,17 @@ +@media (prefers-color-scheme: light) { + tab-item:not(.active):not(.bundled-active):not(.highlighted), + #background { + --toolbar-non-lwt-bgcolor: ThreeDShadow; + --toolbar-non-lwt-textcolor: ButtonText; + } +} + +@media (prefers-color-scheme: dark) { + #tabbar { + background-color: var(--in-content-page-background); + } +} + +tab-item.coloredTabsHue0 tab-item-substance { + background: transparent; +} diff --git a/home-manager/modules/firefox/userChrome.css b/users/jalr/modules/firefox/userChrome.css similarity index 92% rename from home-manager/modules/firefox/userChrome.css rename to users/jalr/modules/firefox/userChrome.css index dcc6b2b..889027a 100644 --- a/home-manager/modules/firefox/userChrome.css +++ b/users/jalr/modules/firefox/userChrome.css @@ -218,4 +218,28 @@ url(chrome://browser/content/browser.xhtml) { } /*** End of: Megabar Styler One-Offs ***/ + + /* Hide "Firefox Suggest" in location bar search results */ + .urlbarView-row[label="Firefox Suggest"]::before { + display: none !important + } + .urlbarView-row[label] { + margin-block-start: 4px !important; + } + + /* Hide search button in location bar */ + #identity-box[pageproxystate=invalid] > .identity-box-button, + .searchbar-search-button { + display: none + } + + /* Hide search placeholder in location bar */ + #urlbar-input::placeholder { + color: transparent; + } + + /* Hide back & forward buttons */ + toolbarbutton#back-button, toolbarbutton#forward-button { + display: none; + } } diff --git a/users/jalr/modules/fish.nix b/users/jalr/modules/fish.nix new file mode 100644 index 0000000..f929e20 --- /dev/null +++ b/users/jalr/modules/fish.nix @@ -0,0 +1,206 @@ +{ config, pkgs, ... }: +{ + home.packages = with pkgs; [ + fzf + ]; + programs.fish = { + enable = true; + plugins = [ + { + name = "theme-agnoster"; + src = pkgs.fetchFromGitHub { + owner = "oh-my-fish"; + repo = "theme-agnoster"; + rev = "4c5518c89ebcef393ef154c9f576a52651400d27"; + sha256 = "OFESuesnfqhXM0aij+79kdxjp4xgCt28YwTrcwQhFMU="; + fetchSubmodules = true; + }; + } + { + name = "fzf"; + src = pkgs.fetchFromGitHub { + owner = "jethrokuan"; + repo = "fzf"; + rev = "479fa67d7439b23095e01b64987ae79a91a4e283"; + sha256 = "0k6l21j192hrhy95092dm8029p52aakvzis7jiw48wnbckyidi6v"; + fetchSubmodules = true; + }; + } + ]; + shellAliases = { + ls = if config.programs.lsd.enable then "lsd" else "ls --color=auto"; + crontab = "crontab -i"; + }; + + shellAbbrs = { + lessr = "less -R"; + jqc = "jq -C"; + }; + + #interactiveShellInit = '' + # echo "programs.fish.interactiveShellInit" + #''; + shellInit = '' + # key bindings + bind \cr '__fzf_reverse_isearch' + + # PATH + set -U fish_user_paths $HOME/.local/bin $HOME/.local/bin/pio + + # pass + #set -x PASSWORD_STORE_ENABLE_EXTENSIONS true + set -x AWS_VAULT_BACKEND pass + set -x AWS_VAULT_PASS_PREFIX aws + complete -c pw --no-files -a '(__fish_pass_print_entries)' + + # colors + set -x GCC_COLORS 'error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + + abbr --add v vim + + #alias cal='ncal -b -M' + alias myip='dig +short myip.opendns.com @resolver1.opendns.com' + + history --merge >/dev/null 2>&1 + + # fancy tools + if which lsd > /dev/null 2>&1 + abbr --add l "lsd" + abbr --add ll "lsd -l --date +'%Y-%m-%d %H:%M:%S'" + abbr --add la "lsd -la --date +'%Y-%m-%d %H:%M:%S'" + abbr --add tree "lsd --tree" + abbr --add llt "lsd -l --timesort --date relative -r" + else + abbr --add l ls + abbr --add ll 'ls -l' + abbr --add la 'ls -la' + abbr --add llt 'ls -trl' + end + + if which rg > /dev/null 2>&1 + abbr --add g rg + complete -c g -w rg + else if which ag > /dev/null 2>&1 + abbr --add g ag + complete -c g -w ag + else + abbr --add g 'grep --color=auto' + complete -c g -w grep + end + + + # NixOS direnv + if which direnv > /dev/null + eval (direnv hook fish) + end + + bind \ed 'dirh-fzf' + + # fix too dark color on solarized theme + set -g fish_color_autosuggestion brgreen + + if type -q fish_set_git_author_by_pwd + fish_set_git_author_by_pwd + end + ''; + + functions = { + jqless = { + body = '' + jq -C $argv | less -R + ''; + }; + __cut_commandline = { + description = "cut commandline and paste it later"; + body = '' + set -g commandline_buffer (commandline) + commandline "" + ''; + }; + __postexec = { + onEvent = "fish_postexec"; + body = '' + if test $status -ne 0; and test -z "$hist_cmd"; and test -z "$fish_private_mode" + #$SHELL -c " + history delete --exact --case-sensitive -- $argv[1] + #" & + end + ''; + }; + dirh-nocolor = { + description = "Print the current directory history (the prev and next lists)"; + body = '' + set -l options h/help + argparse -n dirh --max-args=0 $options -- $argv + or return + + if set -q _flag_help + __fish_print_help dirh + return 0 + end + + set -l dirc (count $dirprev) + if test $dirc -gt 0 + set -l dirprev_rev $dirprev[-1..1] + # This can't be (seq $dirc -1 1) because of BSD. + set -l dirnum (seq 1 $dirc) + for i in $dirnum[-1..1] + printf '%s\n' $dirprev_rev[$i] + end + end + + echo $PWD + + set -l dirc (count $dirnext) + if test $dirc -gt 0 + set -l dirnext_rev $dirnext[-1..1] + for i in (seq $dirc) + printf '%s\n' $dirnext_rev[$i] + end + end + ''; + }; + dirh-fzf = { + description = "directory history fuzzy finder"; + body = '' + builtin cd (dirh-nocolor | uniq | fzf) + ''; + }; + }; + }; + + xdg.configFile = { + "fish/completions/mycli.fish".text = '' + complete -e -c mycli + complete -c mycli -f + complete -c mycli -f -s h -l host -d "Host address of the database." + complete -c mycli -f -s P -l port -d "Port number to use for connection." + complete -c mycli -f -s u -l user -d "User name to connect to the database." + complete -c mycli -f -s S -l socket -d "The socket file to use for connection." + complete -c mycli -f -s p -l pass \ + -l password -d "Password to connect to the database." + complete -c mycli -f -s V -l version -d "Output mycli's version." + complete -c mycli -f -s v -l verbose -d "Verbose output." + complete -c mycli -f -s d -l dsn -d "Use DSN configured into the [alias_dsn] section of myclirc file." + complete -c mycli -f -l list-dsn -d "list of DSN configured into the [alias_dsn] section of myclirc file." + + complete -c mycli -f -s t -l table -d "Display batch output in table format." + complete -c mycli -f -l csv -d "Display batch output in CSV format." + complete -c mycli -f -l warn \ + -l no-warn -d "Warn before running a destructive query." + complete -c mycli -f -s e -l execute -d "Execute command and quit." + + + complete -c mycli -f -s h -l host -r -a '(__fish_print_hostnames)' + complete -c mycli -f -s d -l dsn -r -a '(mycli --list-dsn)' + ''; + + "fish/completions/myssh.fish".text = '' + complete -c myssh -f -a '(myssh --list)' + ''; + + "fish/completions/just.fish".source = pkgs.runCommand "just-fish-completions" { } '' + ${pkgs.just}/bin/just --completions fish > $out + ''; + }; +} diff --git a/home-manager/modules/fpv.nix b/users/jalr/modules/fpv.nix similarity index 77% rename from home-manager/modules/fpv.nix rename to users/jalr/modules/fpv.nix index 9cc7d27..b51fb63 100644 --- a/home-manager/modules/fpv.nix +++ b/users/jalr/modules/fpv.nix @@ -2,7 +2,7 @@ lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - master.betaflight-configurator + betaflight-configurator fpvout ]; } diff --git a/users/jalr/modules/freetube.nix b/users/jalr/modules/freetube.nix new file mode 100644 index 0000000..3c45d96 --- /dev/null +++ b/users/jalr/modules/freetube.nix @@ -0,0 +1,7 @@ +{ nixosConfig, ... }: + +{ + programs.freetube = { + inherit (nixosConfig.jalr.gui) enable; + }; +} diff --git a/home-manager/modules/git.nix b/users/jalr/modules/git.nix similarity index 67% rename from home-manager/modules/git.nix rename to users/jalr/modules/git.nix index e7b49bd..3cf4007 100644 --- a/home-manager/modules/git.nix +++ b/users/jalr/modules/git.nix @@ -1,28 +1,59 @@ -{ nixosConfig, pkgs, ... }: - +{ pkgs, ... }: +let + identity.DigitalerDienst = { + name = "Jakob Lechner"; + email = "j.lechner@digitaler-dienst.gmbh"; + }; +in { programs = { git = { enable = true; - userName = nixosConfig.jalr.git.user.name; - userEmail = nixosConfig.jalr.git.user.email; + userName = "Jakob Lechner"; + userEmail = "mail@jalr.de"; signing = { - key = nixosConfig.jalr.gpg.defaultKey; - signByDefault = nixosConfig.jalr.git.signByDefault; + key = "3044E71E3DEFF49B586CF5809BF4FCCB90854DA9"; + signByDefault = false; + }; + diff-so-fancy = { + enable = true; + markEmptyLines = false; }; extraConfig = { init.defaultBranch = "main"; - core.pager = "${pkgs.diff-so-fancy}/bin/diff-so-fancy | less --tabs=4 -RFX"; diff.sops.textconv = "${pkgs.sops}/bin/sops -d"; pull.ff = "only"; alias.find-merge = "!sh -c 'commit=$0 && branch=\${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"; alias.show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"; + color = { + ui = true; + meta = "11"; + frag = "magenta bold"; + func = "146 bold"; + commit = "yellow bold"; + old = "red bold"; + new = "green bold"; + whitespace = "red reverse"; + diff-highlight = { + oldNormal = "red bold"; + oldHighlight = "red bold 52"; + newNormal = "green bold"; + newHighlight = "green bold 22"; + }; + }; + }; + lfs.enable = true; + }; + lazygit = { + enable = true; + settings = { + gui.scrollHeight = 8; }; }; fish = { shellAbbrs = { ga = "git add"; - gam = "git commit --amend"; + gam = "git commit --amend --no-edit"; gap = "git add --patch"; gb = "git branch"; gbd = "git branch --delete"; @@ -35,12 +66,10 @@ gd = "git diff"; gdc = "git diff --cached"; gf = "git fetch"; - ginit = "git init"; gl = "git log"; - gpll = "git pull"; + gpll = "git pull --rebase"; gpsh = "git push"; grb = "git rebase --autostash"; - grbi = "git rebase --autostash --interactive --autosquash \(git merge-base HEAD origin/master\)"; gr = "git restore"; grs = "git restore --staged"; grst = "git reset"; @@ -55,6 +84,7 @@ gswc = "git switch -c"; gwl = "git worktree list"; gwr = "git worktree remove"; + lg = "lazygit"; }; functions = { #function gwa -d 'git worktree add' @@ -95,19 +125,28 @@ end ''; }; - git_pick-commit_merge-base_origin_master = { + git_pick-commit = { description = "fuzzy find a commit hash"; body = '' - git log --oneline (git merge-base HEAD origin/master)..HEAD | ${pkgs.fzf}/bin/fzf --preview='git show (echo {} | cut -d" " -f 1)' --preview-window=top:75% | cut -d" " -f 1 + git log --decorate --oneline --color=always \ + | ${pkgs.fzf}/bin/fzf --ansi --preview='git show --color=always (echo {} | cut -d" " -f 1)' --preview-window=top:75% \ + | cut -d" " -f 1 ''; }; gfix = { description = "git commit --fixup with fuzzy find commmit picker"; body = '' - set commit (git_pick-commit_merge-base_origin_master) + set commit (git_pick-commit) commandline "git commit --fixup=$commit" ''; }; + gi = { + description = "git interactive rebase with fuzzy find commmit picker"; + body = '' + set commit (git_pick-commit) + commandline "git rebase --autostash --interactive --autosquash $commit" + ''; + }; ".g" = { description = "change directory to repository root"; body = '' @@ -150,10 +189,23 @@ end ''; }; + "fish_set_git_author_by_pwd" = { + description = "Set Git identity by PWD"; + body = '' + if string match -n $HOME'/digitaler-dienst/*' $PWD/ > /dev/null + if git rev-parse --git-dir >/dev/null 2>&1 + git config --local user.name >/dev/null || git config --local user.name "${identity.DigitalerDienst.name}" + git config --local user.email >/dev/null || git config --local user.email "${identity.DigitalerDienst.email}" + end + end + ''; + onVariable = "PWD"; + }; }; }; }; home.packages = with pkgs; [ git-crypt + tig ]; } diff --git a/home-manager/modules/gnuradio.nix b/users/jalr/modules/gnuradio.nix similarity index 54% rename from home-manager/modules/gnuradio.nix rename to users/jalr/modules/gnuradio.nix index d3bec15..8a380a7 100644 --- a/home-manager/modules/gnuradio.nix +++ b/users/jalr/modules/gnuradio.nix @@ -1,13 +1,13 @@ { nixosConfig, lib, pkgs, ... }: let - gnuradioEnv = pkgs.gnuradio3_8.override { + gnuradioEnv = pkgs.gnuradio.override { extraPackages = pkgs.lib.attrVals [ "osmosdr" ] - pkgs.gnuradio3_8Packages; + pkgs.gnuradioPackages; }; in -(lib.mkIf nixosConfig.jalr.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = [ gnuradioEnv ]; -}) +} diff --git a/home-manager/modules/graphics/default.nix b/users/jalr/modules/graphics/default.nix similarity index 76% rename from home-manager/modules/graphics/default.nix rename to users/jalr/modules/graphics/default.nix index 802e665..3af9b75 100644 --- a/home-manager/modules/graphics/default.nix +++ b/users/jalr/modules/graphics/default.nix @@ -1,5 +1,3 @@ -{ nixosConfig, ... }: - { imports = [ ./gimp.nix diff --git a/home-manager/modules/graphics/gimp.nix b/users/jalr/modules/graphics/gimp.nix similarity index 100% rename from home-manager/modules/graphics/gimp.nix rename to users/jalr/modules/graphics/gimp.nix diff --git a/home-manager/modules/graphics/inkscape.nix b/users/jalr/modules/graphics/inkscape.nix similarity index 100% rename from home-manager/modules/graphics/inkscape.nix rename to users/jalr/modules/graphics/inkscape.nix diff --git a/home-manager/modules/graphics/krita.nix b/users/jalr/modules/graphics/krita.nix similarity index 100% rename from home-manager/modules/graphics/krita.nix rename to users/jalr/modules/graphics/krita.nix diff --git a/users/jalr/modules/gui.nix b/users/jalr/modules/gui.nix new file mode 100644 index 0000000..4bc6762 --- /dev/null +++ b/users/jalr/modules/gui.nix @@ -0,0 +1,72 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.pointerCursor = { + package = pkgs.gnome-themes-extra; + name = "Adwaita"; + size = lib.mkDefault 16; + }; + + home.packages = with pkgs; [ + evince + exiftool + geeqie + libreoffice-qt6-fresh + mpv + networkmanagerapplet + streamlink + supersonic-wayland + vlc + xdg-utils + xfce.mousepad + ]; + + services.kanshi = + let + internalDisplay = { + criteria = "BOE 0x0BC9 Unknown"; + status = "enable"; + mode = "2560x1600"; + }; + in + { + enable = true; + settings = [ + { + profile.name = "laemmermann"; + profile.outputs = [ + { + criteria = "ViewSonic Corporation VX3276-QHD VSX2403A0490"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "0,0"; + } + { + criteria = "ViewSonic Corporation VX3276-QHD VSX2351A0801"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "2560,0"; + } + (internalDisplay // { position = "5120,0"; }) + ]; + } + { + profile.name = "digitaler-dienst"; + profile.outputs = [ + { + criteria = "Lenovo Group Limited L27q-35 URB5T16W"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "0,0"; + } + { + criteria = "Lenovo Group Limited L27q-35 URB5T174"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "2560,0"; + } + (internalDisplay // { position = "5120,0"; }) + ]; + } + ]; + }; +} diff --git a/users/jalr/modules/jameica.nix b/users/jalr/modules/jameica.nix new file mode 100644 index 0000000..e50c86e --- /dev/null +++ b/users/jalr/modules/jameica.nix @@ -0,0 +1,16 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ + ( + pkgs.jameica.overrideAttrs (_: { + version = "2.11.0-nightly"; + src = pkgs.fetchFromGitHub { + owner = "willuhn"; + repo = "jameica"; + rev = "e51bffc0e42907cbd802a644ab52810e0a36fff8"; + hash = "sha256-0KcT52dh/tJSX6q+uKkRybz33jKnYRTNDo1BftwJLAc="; + }; + }) + ) + ]; +} diff --git a/home-manager/modules/kicad.nix b/users/jalr/modules/kicad.nix similarity index 100% rename from home-manager/modules/kicad.nix rename to users/jalr/modules/kicad.nix diff --git a/users/jalr/modules/lsd/colors-dark.yaml b/users/jalr/modules/lsd/colors-dark.yaml new file mode 100644 index 0000000..40165eb --- /dev/null +++ b/users/jalr/modules/lsd/colors-dark.yaml @@ -0,0 +1,27 @@ +user: 125 +group: 136 +permission: + read: 166 + write: 64 + exec: 160 + exec-sticky: 125 + no-access: 245 + octal: 37 + acl: 37 + context: 245 +date: + hour-old: 64 + day-old: 136 + older: 240 +size: + none: 160 + small: 61 + medium: 37 + large: 33 +inode: + valid: 64 + invalid: 160 +links: + valid: 61 + invalid: 240 +tree-edge: 245 diff --git a/users/jalr/modules/lsd/colors-light.yaml b/users/jalr/modules/lsd/colors-light.yaml new file mode 100644 index 0000000..bbbbe59 --- /dev/null +++ b/users/jalr/modules/lsd/colors-light.yaml @@ -0,0 +1,27 @@ +user: 125 +group: 136 +permission: + read: 166 + write: 64 + exec: 160 + exec-sticky: 125 + no-access: 234 + octal: 37 + acl: 37 + context: 235 +date: + hour-old: 64 + day-old: 136 + older: 240 +size: + none: 160 + small: 61 + medium: 37 + large: 33 +inode: + valid: 61 + invalid: 160 +links: + valid: 61 + invalid: 235 +tree-edge: 235 diff --git a/users/jalr/modules/lsd/default.nix b/users/jalr/modules/lsd/default.nix new file mode 100644 index 0000000..5547aee --- /dev/null +++ b/users/jalr/modules/lsd/default.nix @@ -0,0 +1,34 @@ +{ lib, nixosConfig, ... }: + +lib.mkIf nixosConfig.jalr.workstation.enable { + programs.lsd = { + enable = true; + enableFishIntegration = false; + settings = { + color.theme = "custom"; + }; + icons = { + extension = { + dxf = "📏"; + flac = "🎶"; + json = "🄹"; + md = "📝"; + mp3 = "🎶"; + opus = "🎶"; + scad = "🔩"; + wav = "🎶"; + yaml = "🅈"; + yml = "🅈"; + zip = "📦"; + }; + name = { + ".envrc" = ""; + Justfile = ""; + justfile = ""; + public = "📢"; + }; + }; + }; + xdg.configFile."lsd/colors-light.yaml".source = ./colors-light.yaml; + xdg.configFile."lsd/colors-dark.yaml".source = ./colors-dark.yaml; +} diff --git a/home-manager/modules/sound/easyeffects.nix b/users/jalr/modules/mixxc/default.nix similarity index 58% rename from home-manager/modules/sound/easyeffects.nix rename to users/jalr/modules/mixxc/default.nix index 215ba9a..efde8d1 100644 --- a/home-manager/modules/sound/easyeffects.nix +++ b/users/jalr/modules/mixxc/default.nix @@ -2,6 +2,9 @@ lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - easyeffects + mixxc ]; + xdg.configFile = { + "mixxc/style.css".source = ./style.css; + }; } diff --git a/users/jalr/modules/mixxc/style.css b/users/jalr/modules/mixxc/style.css new file mode 100644 index 0000000..33dbf39 --- /dev/null +++ b/users/jalr/modules/mixxc/style.css @@ -0,0 +1,224 @@ +.side { + $hide: false; + + .output { + transition: background 750ms; + padding: 5px; + + &.master { + transition: background 0ms; + } + + &.master:hover { + } + + .icon { + -gtk-icon-style: symbolic; + -gtk-icon-size: 16px; + } + } + + @if $hide { + min-height: 0; + min-width: 0; + + .output { + padding: 0; + + .icon { + -gtk-icon-style: symbolic; + -gtk-icon-size: 0; + } + } + } +} + +.main { + margin: 20px; +} + +.client { + $hide-name: false; + $hide-description: false; + + font-family: 'Iosevka Nerd Font'; + font-size: 1.2em; + + .icon { + -gtk-icon-style: symbolic; + } + + @if $hide-name { + .name { + font-size: 0; + } + } + + @if $hide-description { + .description { + font-size: 0; + } + } + + scale { + trough { + /* Slider Bar */ + border-radius: 10px; + + slider { + /* Slider Knob */ + padding: 0; + + border: none; + border-radius: 2px; + + transition-duration: 400ms; + } + + highlight { + /* Slider Bar Filled */ + border: none; + border-radius: 10px; + + margin: 2px; + + transition: background-image 300ms; + } + + fill { + /* Slider Peak */ + background: none; + + border-radius: 10px; + + margin: 0px; + } + } + } + + scale:active { + trough slider { + /* Slider Knob */ + transform: scale(1.1); + } + } +} + +.client.horizontal { + &.new { + animation: client-add-horizontal 300ms ease; + } + + &.removed { + animation: client-remove-horizontal 300ms ease; + } + + .icon { + padding-right: 13px; + + -gtk-icon-size: 16px; + } + + .volume { + /* Numeric Volume Level */ + padding-left: 22px; + padding-bottom: 3px; + } + + scale { + trough { + /* Slider Bar */ + min-height: 6px; + + slider { + /* Slider Knob */ + min-height: 21px; + min-width: 9px; + + margin-top: -7px; + margin-bottom: -7px; + } + } + } + +} + +@keyframes client-add-horizontal { + from { + transform: translateX(-200px); + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes client-remove-horizontal { + from { + opacity: 1; + } + to { + transform: translateX(-200px); + opacity: 0; + } +} + +.client.vertical { + &.new { + animation: client-add-vertical 300ms ease; + } + + &.removed { + animation: client-remove-vertical 300ms ease; + } + + .icon { + padding-bottom: 5px; + + -gtk-icon-size: 20px; + } + + .volume { + /* Numeric Volume Level */ + padding-top: 10px; + } + + scale { + trough { + /* Slider Bar */ + min-width: 4px; + + margin-top: 10px; + + slider { + /* Slider Knob */ + margin-left: -7px; + margin-right: -7px; + + min-height: 6px; + min-width: 14px; + } + } + } +} + +@keyframes client-add-vertical { + from { + transform: translateY(200px); + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes client-remove-vertical { + from { + opacity: 1; + } + to { + transform: translateY(200px); + opacity: 0; + } +} + diff --git a/home-manager/modules/mpv.nix b/users/jalr/modules/mpv.nix similarity index 100% rename from home-manager/modules/mpv.nix rename to users/jalr/modules/mpv.nix diff --git a/home-manager/modules/mute-indicator.nix b/users/jalr/modules/mute-indicator.nix similarity index 100% rename from home-manager/modules/mute-indicator.nix rename to users/jalr/modules/mute-indicator.nix diff --git a/users/jalr/modules/mycli/default.nix b/users/jalr/modules/mycli/default.nix new file mode 100644 index 0000000..844102b --- /dev/null +++ b/users/jalr/modules/mycli/default.nix @@ -0,0 +1,173 @@ +{ lib, pkgs, ... }: +let + quoteValues = ini: lib.mapAttrs + (_: attrs: + lib.mapAttrs (_: value: if builtins.isString value then ''"${value}"'' else value) attrs + ) + ini; + + solarized = import ../solarized.nix; + + config = { + main = { + # Enables context sensitive auto-completion. If this is disabled the all + # possible completions will be listed. + smart_completion = true; + + # Multi-line mode allows breaking up the sql statements into multiple lines. If + # this is set to True, then the end of the statements must have a semi-colon. + # If this is set to False then sql statements can't be split into multiple + # lines. End of line (return) is considered as the end of the statement. + multi_line = false; + + # Destructive warning mode will alert you before executing a sql statement + # that may cause harm to the database such as "drop table", "drop database" + # or "shutdown". + destructive_warning = true; + + # log_file location. + log_file = "~/.mycli.log"; + + # Default log level. Possible values: "CRITICAL", "ERROR", "WARNING", "INFO" + # and "DEBUG". "NONE" disables logging. + log_level = "INFO"; + + # Log every query and its results to a file. Enable this by uncommenting the + # line below. + # audit_log = "~/.mycli-audit.log"; + + # Timing of sql statments and table rendering. + timing = true; + + # Table format. Possible values: ascii, double, github, + # psql, plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, + # latex, latex_booktabs, textile, moinmoin, jira, vertical, tsv, csv. + # Recommended: ascii + table_format = "pipe"; + + # Syntax coloring style. Possible values (many support the "-dark" suffix): + # manni, igor, xcode, vim, autumn, vs, rrt, native, perldoc, borland, tango, emacs, + # friendly, monokai, paraiso, colorful, murphy, bw, pastie, paraiso, trac, default, + # fruity. + # Screenshots at http://mycli.net/syntax + # Can be further modified in [colors] + syntax_style = "native"; + + # Keybindings: Possible values: emacs, vi. + # Emacs mode: Ctrl-A is home, Ctrl-E is end. All emacs keybindings are available in the REPL. + # When Vi mode is enabled you can use modal editing features offered by Vi in the REPL. + key_bindings = "emacs"; + + # Enabling this option will show the suggestions in a wider menu. Thus more items are suggested. + wider_completion_menu = false; + + # MySQL prompt + # \D - The full current date + # \d - Database name + # \h - Hostname of the server + # \m - Minutes of the current time + # \n - Newline + # \P - AM/PM + # \p - Port + # \R - The current time, in 24-hour military time (0–23) + # \r - The current time, standard 12-hour time (1–12) + # \s - Seconds of the current time + # \t - Product type (Percona, MySQL, MariaDB) + # \A - DSN alias name (from the [alias_dsn] section) + # \u - Username + # \x1b[...m - insert ANSI escape sequence + prompt = "\\u@\\A:\\d> "; + prompt_continuation = "-> "; + + # Skip intro info on startup and outro info on exit + less_chatty = false; + + # Use alias from --login-path instead of host name in prompt + login_path_as_host = false; + + # Cause result sets to be displayed vertically if they are too wide for the current window, + # and using normal tabular format otherwise. (This applies to statements terminated by ; or \G.) + auto_vertical_output = false; + + # keyword casing preference. Possible values "lower", "upper", "auto" + keyword_casing = "upper"; + + # disabled pager on startup + enable_pager = true; + }; + # Favorite queries. + favorite_queries = { }; + + # Use the -d option to reference a DSN. + # Special characters in passwords and other strings can be escaped with URL encoding. + alias_dsn = { + # example_dsn = "mysql://[user[:password]@][host][:port][/dbname]"; + }; + }; + + colors = + let + c = solarized.colors; + in + { + common = { + "output.header" = "bold ${c.green}"; + "sql.datatype" = "nobold ${c.yellow}"; + "sql.function" = "bold ${c.violet}"; + "sql.keyword" = c.green; + "sql.literal" = c.green; + "sql.number" = c.cyan; + "sql.string" = c.cyan; + "sql.variable" = c.red; + "sql.quoted-schema-object" = c.blue; + }; + light = { + "prompt" = "bg:${c.blue} ${c.base02}"; + "selected" = "bg:${c.base2} ${c.base00}"; + "output.odd-row" = "${c.base01}"; + "output.even-row" = "${c.base01} bg:${c.base2}"; + "sql.comment" = "italic ${c.base1}"; + "sql.operator" = "bold ${c.base02}"; + "sql.punctuation" = "bold ${c.base01}"; + "sql.symbol" = "${c.base01}"; + }; + dark = { + "prompt" = "bg:${c.blue} ${c.base2}"; + "selected" = "bg:${c.base02} ${c.base0}"; + "output.odd-row" = "${c.base1}"; + "output.even-row" = "${c.base1} bg:${c.base02}"; + "sql.comment" = "italic ${c.base01}"; + "sql.operator" = "bold ${c.base2}"; + "sql.punctuation" = "bold ${c.base1}"; + "sql.symbol" = "${c.base1}"; + }; + }; +in +{ + home.packages = [ + (pkgs.mycli.overridePythonAttrs (old: { + dependencies = old.dependencies ++ [ pkgs.python3Packages.sshtunnel ]; + })) + (pkgs.stdenv.mkDerivation { + name = "myssh"; + propagatedBuildInputs = [ + (pkgs.python3.withPackages (pp: with pp; [ + pyyaml + ])) + ]; + dontUnpack = true; + installPhase = "install -Dm755 ${./myssh.py} $out/bin/myssh"; + }) + ]; + + xdg.configFile = lib.attrsets.mapAttrs' + ( + name: value: lib.attrsets.nameValuePair "mycli/theme-${name}.ini" { + text = lib.generators.toINI { } (quoteValues (config // { colors = value; })); + } + ) + { + light = colors.common // colors.light; + dark = colors.common // colors.dark; + }; +} diff --git a/users/jalr/modules/mycli/myssh.py b/users/jalr/modules/mycli/myssh.py new file mode 100755 index 0000000..c79b430 --- /dev/null +++ b/users/jalr/modules/mycli/myssh.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +import argparse +import json +import os +import subprocess +import sys +import yaml + + +def get_db_connecition_from_typo3_config(ssh_host, ssh_user, config_file): + php_code = """ + 1 + } + + +def connect(connection_name, connection): + if "t3_config" in connection: + db = get_db_connecition_from_typo3_config( + connection["ssh_host"], connection["ssh_user"], connection["t3_config"] + ) + db_host = db["host"] + db_user = db["user"] + db_password = db["password"] + db_name = db["dbname"] + elif "staging_level" in connection: + env = get_db_connection_from_env_file( + connection["ssh_host"], connection["ssh_user"], connection["staging_level"] + ) + db_host = env["TYPO3_DB_HOST"] + db_user = env["TYPO3_DB_USER"] + db_password = env["TYPO3_DB_PASSWORD"] + db_name = env["TYPO3_DB_DATABASE"] + + os.execl( + "/usr/bin/env", + "env", + "mycli", + "--prompt", + f"{connection_name}>", + "--ssh-user", + connection["ssh_user"], + "--ssh-host", + connection["ssh_host"], + "-h", + db_host, + "-u", + db_user, + "-p", + db_password, + db_name, + ) + + +def read_config(path): + with open(path, "r") as fh: + config = yaml.safe_load(fh.read()) + + return config + + +def main(): + connections = read_config(os.path.expanduser("~/.config/mycli/connections.yaml")) + + parser = argparse.ArgumentParser() + parser.add_argument("--list", action="store_true") + parser.add_argument("connection", type=str, nargs="?") + + args = vars(parser.parse_args()) + + if args["list"]: + print("\n".join(connections.keys())) + + if args["connection"] is not None: + connect(args["connection"], connections[args["connection"]]) + + +if __name__ == "__main__": + main() diff --git a/users/jalr/modules/neovim/default.nix b/users/jalr/modules/neovim/default.nix new file mode 100644 index 0000000..03b8b0b --- /dev/null +++ b/users/jalr/modules/neovim/default.nix @@ -0,0 +1,305 @@ +{ lib, nixosConfig, config, pkgs, ... }: + +let + fakePlugin = pkgs.runCommand "neovim-fake-plugin" { } "mkdir $out"; +in +{ + programs.neovim = { + enable = true; + vimAlias = true; + defaultEditor = true; + extraConfig = '' + """"""""""""""""" + " Swap and undo " + set noswapfile + set nobackup + if has('persistent_undo') + " yay persistent undo + :silent !mkdir -p ~/.local/vim-undo + set undofile + set undodir=~/.local/vim-undo + endif + + cabbr %% expand('%:p:h') + + set listchars=trail:·,precedes:«,extends:»,eol:↲,tab:▸\ + nmap c :set list! + + set smartcase + set hlsearch + nnoremap :nohlsearch:set nolist + + " highlight whitespace + highlight ExtraWhitespace ctermbg=red guibg=red + highlight WrongIndent ctermbg=2 guibg=blue + match ExtraWhitespace /\s\+$/ + augroup highlight_extra_whitespace + autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ + autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ e lua vim.diagnostic.enable(not vim.diagnostic.is_enabled()) + nnoremap i lua vim.diagnostic.open_float() + nnoremap gd lua vim.lsp.buf.definition() + nnoremap gi lua vim.lsp.buf.implementation() + nnoremap gr lua vim.lsp.buf.references() + nnoremap gD lua vim.lsp.buf.declaration() + nnoremap ge lua vim.lsp.diagnostic.set_loclist() + nnoremap K lua vim.lsp.buf.hover() + nnoremap f lua vim.lsp.buf.formatting() + nnoremap rn lua vim.lsp.buf.rename() + + nnoremap dh lua require('dap.ui.widgets').hover() + nnoremap dp lua require('dap.ui.widgets').preview() + nnoremap dc lua require('dap').continue() + nnoremap do lua require('dap').step_over() + nnoremap di lua require('dap').step_into() + nnoremap dn lua require('dap').step_out() + nnoremap b lua require('dap').toggle_breakpoint() + + nnoremap a lua vim.lsp.buf.code_action() + xmap a lua vim.lsp.buf.range_code_action() + + lua require('init') + ''; + + plugins = [ + { + plugin = fakePlugin; + config = '' + " use space as leader + let mapleader = " " + ''; + } + ] ++ + # nix-env -f '' -qaP -A vimPlugins + (with pkgs.vimPlugins; [ + { + plugin = NeoSolarized; + config = '' + colorscheme NeoSolarized + ''; + } + { + plugin = nvim-dap; + type = "lua"; + config = '' + local dap = require('dap') + dap.adapters.php = { + type = "executable", + command = "${pkgs.nodejs}/bin/node", + args = { "${pkgs.vscode-extensions.xdebug.php-debug}/share/vscode/extensions/xdebug.php-debug/out/phpDebug.js" } + } + + dap.configurations.php = { + { + type = "php", + request = "launch", + name = "Listen for Xdebug", + port = 9003, + --stopOnEntry = true, + pathMappings = { + ["/app/"] = vim.fn.getcwd().."/", + }, + log = true, + hostname = "0.0.0.0", + } + } + + vim.api.nvim_create_autocmd( + "FileType", { + pattern = "dap-float", + callback = function() + vim.api.nvim_buf_set_keymap(0, "n", "q", "close!", { noremap = true, silent = true }) + end + } + ) + ''; + } + deoplete-nvim + editorconfig-vim + jinja-vim + nvim-lspconfig + { + plugin = telescope-nvim; + type = "lua"; + config = '' + require('telescope').setup() + vim.keymap.set('n', '', 'Telescope find_files') + vim.keymap.set('n', '', 'Telescope live_grep') + vim.keymap.set('n', '', 'Telescope buffers') + ''; + } + { + plugin = telescope-ultisnips-nvim; + type = "lua"; + config = '' + require('telescope').load_extension('ultisnips') + ''; + } + nvim-treesitter-parsers.twig + { + plugin = nvim-treesitter; + type = "lua"; + config = '' + require'nvim-treesitter.configs'.setup { highlight = { enable = true, }, } + ''; + } + { + plugin = ultisnips; + config = ''; + let g:UltiSnipsSnippetDirectories = [ "UltiSnips" ] + inoremap :Telescope ultisnips + ''; + } + { + plugin = vim-fluid; + config = '' + au BufRead *.html if join(getline(1,3), "\n") =~ 'data-namespace-typo3-fluid="true"' | setlocal filetype=fluid | endif + ''; + } + vim-gitgutter + vim-indent-guides + vim-nix + vim-terraform + vim-typoscript + ( + lib.mkIf nixosConfig.jalr.workstation.enable { + plugin = lsp_signature-nvim; + type = "lua"; + config = "require('lsp_signature').setup()"; + } + ) + ]); + }; + + xdg.configFile = { + "nvim/ftplugin/gitcommit.vim".text = '' + setlocal spell + setlocal colorcolumn=73 + ''; + "nvim/ftplugin/markdown.vim".text = '' + setlocal spell + setlocal colorcolumn=81 + ''; + "nvim/ftplugin/sshconfig.vim".text = '' + setlocal tabstop=4 shiftwidth=4 softtabstop=4 expandtab + ''; + "nvim/lua/init.lua".text = builtins.concatStringsSep "\n" ( + [ + '' + -- init.lua + -- this configuration applies to servers and workstations + '' + ] ++ lib.optional nixosConfig.jalr.workstation.enable ( + '' + -- this configuration applies to workstations only + -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md + local lsp = require('lspconfig') + + -- show linter messages + vim.diagnostic.config({ virtual_text = true }) + '' + + builtins.concatStringsSep "\n" ( + lib.mapAttrsToList + ( + lang: cfg: "lsp.${lang}.setup\n" + lib.generators.toLua { } cfg + ) + { + # C and C++ + ccls = { + cmd = [ "${pkgs.ccls}/bin/ccls" ]; + }; + + # Nix + nixd = { + cmd = [ "${pkgs.nixd}/bin/nixd" ]; + }; + + # PHP + phpactor = { + cmd = [ "${pkgs.phpactor}/bin/phpactor" "language-server" ]; + init_options = { + "language_server.diagnostic_ignore_codes" = [ + "worse.docblock_missing_param" + "worse.docblock_missing_return_type" + ]; + }; + }; + + # Python + pylsp = { + cmd = [ "${pkgs.python3Packages.python-lsp-server}/bin/pylsp" ]; + settings = { + # https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md + pylsp = { + plugins = { + flake8 = { + enabled = true; + executable = "${pkgs.python3Packages.flake8}/bin/flake8"; + }; + jedi_completion = { enabled = true; }; + jedi_definition = { enabled = true; }; + jedi_hover = { enabled = true; }; + jedi_references = { enabled = true; }; + jedi_signature_help = { enabled = true; }; + jedi_symbols = { enabled = true; }; + mccabe = { enabled = true; }; + lsp_signature-nvim = { enabled = true; }; + preload = { enabled = true; }; + pycodestyle = { enabled = true; }; + pyflakes = { enabled = true; }; + rope_completion = { enabled = true; }; + yapf = { enabled = true; }; + }; + }; + }; + }; + + # Ruby + solargraph = { + cmd = [ "${pkgs.solargraph}/bin/solargraph" "stdio" ]; + }; + + # Rust + rust_analyzer = { + cmd = [ "${pkgs.rust-analyzer}/bin/rust-analyzer" ]; + }; + + # Bash + bashls = { + cmd = [ "${pkgs.nodePackages.bash-language-server}/bin/bash-language-server" "start" ]; + }; + + # Terraform + terraformls = { + cmd = [ "${pkgs.terraform-ls}/bin/terraform-ls" "serve" ]; + }; + + # YAML + yamlls = { + cmd = [ "${pkgs.nodePackages.yaml-language-server}/bin/yaml-language-server" "--stdio" ]; + settings = { + yaml = { + keyOrdering = false; + }; + }; + }; + } + ) + ) + ); + }; +} diff --git a/users/jalr/modules/nix-index.nix b/users/jalr/modules/nix-index.nix new file mode 100644 index 0000000..c753a5e --- /dev/null +++ b/users/jalr/modules/nix-index.nix @@ -0,0 +1,5 @@ +{ + programs.nix-index = { + enable = true; + }; +} diff --git a/home-manager/modules/obs-studio/default.nix b/users/jalr/modules/obs-studio/default.nix similarity index 75% rename from home-manager/modules/obs-studio/default.nix rename to users/jalr/modules/obs-studio/default.nix index 545bcbd..ffd39b3 100644 --- a/home-manager/modules/obs-studio/default.nix +++ b/users/jalr/modules/obs-studio/default.nix @@ -2,7 +2,7 @@ { programs.obs-studio = { - enable = nixosConfig.jalr.gui.enable; + inherit (nixosConfig.jalr.gui) enable; plugins = with pkgs; [ obs-studio-plugins.wlrobs ]; diff --git a/users/jalr/modules/ots.nix b/users/jalr/modules/ots.nix new file mode 100644 index 0000000..4f3e85c --- /dev/null +++ b/users/jalr/modules/ots.nix @@ -0,0 +1,49 @@ +{ pkgs, ... }: + +{ + home.packages = [ + ( + pkgs.writeShellScriptBin "ots" '' + set -e + set -o nounset + + args=() + region="" + new="" + while [[ $# -gt 0 ]]; do + case "$1" in + new) + new=1 + args+=("$1"); shift + ;; + --region|--region=*) + region=1 + args+=("$1"); shift + ;; + *) + args+=("$1"); shift + ;; + esac + done + + if [[ $new && ! $region ]]; then + args+=("--region" "eu-central-1") + fi + + exec ${pkgs.ots}/bin/ots "''${args[@]}" + '' + ) + ]; + + /* + xdg.configFile."${configFile}".text = lib.generators.toJSON {} ( + let + region = "eu-central-1"; + in + { + apiUrl = "https://ots.${region}.api.sniptt.com/secrets"; + apiKey = ""; + } + ); + */ +} diff --git a/users/jalr/modules/pace.nix b/users/jalr/modules/pace.nix new file mode 100644 index 0000000..0a0b43f --- /dev/null +++ b/users/jalr/modules/pace.nix @@ -0,0 +1,28 @@ +{ config, nixosConfig, lib, pkgs, ... }: + + +let + tomlFormat = pkgs.formats.toml { }; +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + pace + ]; + + home.sessionVariables.PACE_HOME = "${config.xdg.configHome}/pace"; + + xdg.configFile."pace/pace.toml".source = tomlFormat.generate "pace.toml" { + general = { + path = "${config.home.homeDirectory}/.local/share/pace/activities/activities.pace.toml"; + storage-kind = "file"; + category-separator = "::"; + default-priority = "medium"; + most-recent-count = 9; + default-time-zone = "${nixosConfig.time.timeZone}"; + }; + }; + + programs.fish.interactiveShellInit = lib.mkAfter '' + ${pkgs.pace}/bin/pace setup completions fish | source + ''; +} diff --git a/users/jalr/modules/pass.nix b/users/jalr/modules/pass.nix new file mode 100644 index 0000000..e77fdb9 --- /dev/null +++ b/users/jalr/modules/pass.nix @@ -0,0 +1,18 @@ +{ nixosConfig, lib, config, pkgs, ... }: + +{ + programs.password-store = { + enable = true; + package = + if nixosConfig.jalr.gui.enable + then pkgs.pass-wayland.withExtensions (exts: [ exts.pass-otp ]) + else pkgs.pass.withExtensions (exts: [ exts.pass-otp ]); + }; + home.packages = lib.optionals nixosConfig.jalr.gui.enable [ + pkgs.qtpass + ]; + xdg.dataFile.password-store = { + source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.password-store"; + target = "password-store"; + }; +} diff --git a/users/jalr/modules/pomodoro.nix b/users/jalr/modules/pomodoro.nix new file mode 100644 index 0000000..6df182a --- /dev/null +++ b/users/jalr/modules/pomodoro.nix @@ -0,0 +1,70 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + tomlFormat = pkgs.formats.toml { }; +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + uair + pomodoro-timer + ]; + + xdg.configFile."uair/uair.toml".source = tomlFormat.generate "uair.toml" { + defaults = { + loop_on_end = true; + paused_state_text = "paused"; + resumed_state_text = "resumed"; + overrides = { + yad = { + format = "{percent}\n#{time}\n"; + }; + json = { + format = ''{"name": "{name}", "percent": {percent}", "time": "{time}", "running": {state}, "total": "{total}"} + ''; + paused_state_text = "false"; + resumed_state_text = "true"; + }; + waybar = { + format = ''{"text": "{name}: {time}", "class": "{state}"} + ''; + paused_state_text = "paused"; + resumed_state_text = "resumed"; + }; + }; + }; + sessions = + let + notify-send = "notify-send --app-name='Pomodoro timer' --icon='pomodorotimer'"; + work-finished = "${notify-send} 'Pomodoro done! Enjoy your break.'; dnd off"; + break-finished = "${notify-send} 'Get back to work!'; dnd on"; + in + [ + { name = "Work 1"; duration = "25m"; command = work-finished; } + { name = "Break 1"; duration = "5m"; command = break-finished; } + { name = "Work 2"; duration = "25m"; command = work-finished; } + { name = "Break 2"; duration = "5m"; command = break-finished; } + { name = "Work 3"; duration = "25m"; command = work-finished; } + { name = "Break 3"; duration = "5m"; command = break-finished; } + { name = "Work 4"; duration = "25m"; command = work-finished; } + { name = "Long Break"; duration = "20m"; command = break-finished; } + ]; + }; + + systemd.user.services.uair = { + Unit.Description = "Pomodoro timer"; + Service = { + ExecStart = "${pkgs.uair}/bin/uair"; + NoNewPrivileges = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + Type = "simple"; + Environment = [ + "PATH=/etc/profiles/per-user/jalr/bin:/run/current-system/sw/bin/" + ]; + }; + Install.WantedBy = [ "default.target" ]; + }; +} diff --git a/home-manager/modules/python.nix b/users/jalr/modules/python.nix similarity index 55% rename from home-manager/modules/python.nix rename to users/jalr/modules/python.nix index 9dbf3e2..d73b0e3 100644 --- a/home-manager/modules/python.nix +++ b/users/jalr/modules/python.nix @@ -1,8 +1,10 @@ { nixosConfig, lib, pkgs, ... }: lib.mkIf nixosConfig.jalr.workstation.enable { home.packages = with pkgs; [ - python310 - python310Packages.virtualenv - python310Packages.ipython + (python3.withPackages (pp: with pp; [ + ipython + pyyaml + virtualenv + ])) ]; } diff --git a/users/jalr/modules/remarkable/default.nix b/users/jalr/modules/remarkable/default.nix new file mode 100644 index 0000000..73bffe8 --- /dev/null +++ b/users/jalr/modules/remarkable/default.nix @@ -0,0 +1,8 @@ +{ nixosConfig, lib, ... }: + +{ + imports = lib.optionals nixosConfig.jalr.gui.enable [ + ./restream.nix + ./rmview.nix + ]; +} diff --git a/users/jalr/modules/remarkable/restream.nix b/users/jalr/modules/remarkable/restream.nix new file mode 100644 index 0000000..6dcbec5 --- /dev/null +++ b/users/jalr/modules/remarkable/restream.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + home.packages = with pkgs; [ + restream + ]; +} diff --git a/users/jalr/modules/remarkable/rmview.nix b/users/jalr/modules/remarkable/rmview.nix new file mode 100644 index 0000000..0bf96ed --- /dev/null +++ b/users/jalr/modules/remarkable/rmview.nix @@ -0,0 +1,28 @@ +{ lib, pkgs, ... }: + +let + config = { + ssh = { + address = "remarkable"; + auth_method = "key"; + key = "~/.ssh/id_rsa"; + }; + orientation = "auto"; + pen_size = 15; + pen_color = "red"; + pen_trail = 200; + }; +in +{ + home.packages = with pkgs; [ + ( + writeShellScriptBin "rmview" '' + export QT_QPA_PLATFORM=xcb + exec ${pkgs.rmview}/bin/rmview "$@" + '' + ) + ]; + xdg.configFile."rmview.json" = { + text = lib.generators.toJSON { } config; + }; +} diff --git a/home-manager/modules/jameica.nix b/users/jalr/modules/roomeqwizard.nix similarity index 86% rename from home-manager/modules/jameica.nix rename to users/jalr/modules/roomeqwizard.nix index d9472e0..42b49aa 100644 --- a/home-manager/modules/jameica.nix +++ b/users/jalr/modules/roomeqwizard.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - jameica + roomeqwizard ]; } diff --git a/users/jalr/modules/snapclient.nix b/users/jalr/modules/snapclient.nix new file mode 100644 index 0000000..c391bfb --- /dev/null +++ b/users/jalr/modules/snapclient.nix @@ -0,0 +1,20 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + systemd.user.services.snapclient = { + Unit.Description = "Snapcast client"; + Service = { + BindPaths = [ "/run/user/1000/pulse" ]; + ExecStart = "${pkgs.snapcast}/bin/snapclient --player pulse"; + NoNewPrivileges = true; + ProtectControlGroups = true; + ProtectHome = "tmpfs"; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + Type = "simple"; + }; + Install.WantedBy = [ "default.target" ]; + }; +} diff --git a/users/jalr/modules/solarized.nix b/users/jalr/modules/solarized.nix new file mode 100644 index 0000000..baa2547 --- /dev/null +++ b/users/jalr/modules/solarized.nix @@ -0,0 +1,68 @@ +let + colors = { + base00 = "#657b83"; + base01 = "#586e75"; + base02 = "#073642"; + base03 = "#002b36"; + base0 = "#839496"; + base1 = "#93a1a1"; + base2 = "#eee8d5"; + base3 = "#fdf6e3"; + blue = "#268bd2"; + cyan = "#2aa198"; + green = "#859900"; + magenta = "#d33682"; + orange = "#cb4b16"; + red = "#dc322f"; + violet = "#6c71c4"; + yellow = "#b58900"; + }; + common = { + base08 = colors.red; + base09 = colors.orange; + base0A = colors.yellow; + base0B = colors.green; + base0C = colors.cyan; + base0D = colors.blue; + base0E = colors.violet; + base0F = colors.magenta; + }; + light = common // { + base00 = colors.base3; + base01 = colors.base2; + base02 = colors.base1; + base03 = colors.base0; + base04 = colors.base00; + base05 = colors.base01; + base06 = colors.base02; + base07 = colors.base03; + }; + dark = common // { + base00 = colors.base03; + base01 = colors.base02; + base02 = colors.base01; + base03 = colors.base00; + base04 = colors.base0; + base05 = colors.base1; + base06 = colors.base2; + base07 = colors.base3; + }; + toRgb = hex: builtins.concatStringsSep "," ( + map + ( + f: toString (builtins.fromTOML "i = 0x${f hex}").i + ) + ( + map (pos: builtins.substring pos 2) [ 1 3 5 ] + ) + ); + makeScheme = colors: { + hex = colors; + rgb = builtins.mapAttrs (_: hex: (toRgb hex)) colors; + }; +in +{ + inherit colors; + light = makeScheme light; + dark = makeScheme dark; +} diff --git a/home-manager/modules/sound/audacity.nix b/users/jalr/modules/sound/audacity.nix similarity index 100% rename from home-manager/modules/sound/audacity.nix rename to users/jalr/modules/sound/audacity.nix diff --git a/home-manager/modules/sound/default.nix b/users/jalr/modules/sound/default.nix similarity index 59% rename from home-manager/modules/sound/default.nix rename to users/jalr/modules/sound/default.nix index 5c7de31..d5764d3 100644 --- a/home-manager/modules/sound/default.nix +++ b/users/jalr/modules/sound/default.nix @@ -1,8 +1,8 @@ -{ nixosConfig, ... }: - { imports = [ ./audacity.nix + ./easyeffects.nix ./pipewire.nix + #./ksoloti.nix ]; } diff --git a/users/jalr/modules/sound/easyeffects.nix b/users/jalr/modules/sound/easyeffects.nix new file mode 100644 index 0000000..eaad63c --- /dev/null +++ b/users/jalr/modules/sound/easyeffects.nix @@ -0,0 +1,14 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + easyeffects + ]; + + services.easyeffects.enable = true; + + xdg.configFile."easyeffects/output/framework-16.json".source = pkgs.fetchurl { + url = "https://gist.githubusercontent.com/amesb/cc5d717472d7e322b5f551b643ff03f4/raw/85029e48072ab3802615b2824dce7df204f0d8ab/amesb%2520fw16%2520EE%2520profile.json"; + sha256 = "sha256-Te8S9DsG5P/NuNk5WE6mSB/DjHS+rKjOFRN7mDEVg8g="; + }; +} diff --git a/home-manager/modules/communication/qtox.nix b/users/jalr/modules/sound/ksoloti.nix similarity index 90% rename from home-manager/modules/communication/qtox.nix rename to users/jalr/modules/sound/ksoloti.nix index 85aa45b..3528602 100644 --- a/home-manager/modules/communication/qtox.nix +++ b/users/jalr/modules/sound/ksoloti.nix @@ -2,6 +2,6 @@ lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - qtox + ksoloti ]; } diff --git a/home-manager/modules/sound/pipewire.nix b/users/jalr/modules/sound/pipewire.nix similarity index 89% rename from home-manager/modules/sound/pipewire.nix rename to users/jalr/modules/sound/pipewire.nix index 0be3f41..22503f8 100644 --- a/home-manager/modules/sound/pipewire.nix +++ b/users/jalr/modules/sound/pipewire.nix @@ -2,7 +2,6 @@ lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - easyeffects pavucontrol qpwgraph ]; diff --git a/home-manager/modules/sway/default.nix b/users/jalr/modules/sway/default.nix similarity index 67% rename from home-manager/modules/sway/default.nix rename to users/jalr/modules/sway/default.nix index 8b51241..7e4e764 100644 --- a/home-manager/modules/sway/default.nix +++ b/users/jalr/modules/sway/default.nix @@ -1,24 +1,21 @@ -{ nixosConfig, config, lib, pkgs, stdenv, ... }: +{ nixosConfig, config, lib, pkgs, ... }: let solarized = import ../solarized.nix; terminalEmulator = - if nixosConfig.jalr.terminalEmulator == "alacritty" - then - pkgs.writeShellScript "alacritty-sway-cwd" '' - this_alacritty_pid="$(${pkgs.sway}/bin/swaymsg -t get_tree | ${pkgs.jq}/bin/jq -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="Alacritty")).pid')" + pkgs.writeShellScript "wezterm-sway-cwd" '' + this_wezterm_pid="$(${pkgs.sway}/bin/swaymsg -t get_tree --raw | ${pkgs.jq}/bin/jq -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="org.wezfurlong.wezterm")).pid')" - if [ "$this_alacritty_pid" ]; then - child_pid="$(pgrep -P "$this_alacritty_pid")" - cwd="$(readlink /proc/$child_pid/cwd)" - fi - if [ -e "$cwd" ]; then - exec ${pkgs.alacritty}/bin/alacritty --working-directory "$cwd" - fi + if [ "$this_wezterm_pid" ]; then + child_pid="$(pgrep -P "$this_wezterm_pid")" + cwd="$(readlink /proc/$child_pid/cwd)" + fi + if [ -e "$cwd" ]; then + exec ${pkgs.wezterm}/bin/wezterm start --cwd "$cwd" + fi - exec ${pkgs.alacritty}/bin/alacritty - '' - else nixosConfig.jalr.terminalEmulator; + exec ${pkgs.wezterm}/bin/wezterm + ''; cfg = config.wayland.windowManager.sway.config; wallpaper = pkgs.fetchurl { url = "https://raw.githubusercontent.com/swaywm/sway/3b2bc894a5ebbcbbd6707d45a25d171779c2e874/assets/Sway_Wallpaper_Blue_1920x1080.png"; @@ -27,18 +24,59 @@ let meta.license = lib.licenses.cc0; }; move-to-output = pkgs.callPackage ./move-to-output { }; + gsettings = + let + schema = pkgs.gsettings-desktop-schemas; + datadir = "${schema}/share/gsettings-schemas/${schema.name}"; + in + pkgs.writeShellScriptBin "gsettings" '' + export XDG_DATA_DIRS=${datadir}:$XDG_DATA_DIRS + gnome_schema=org.gnome.desktop.interface + #gsettings set $gnome_schema gtk-theme 'Dracula' + ${pkgs.glib}/bin/gsettings "$@" + ''; + matchHostname = hostname: lib.optionalAttrs (nixosConfig.networking.hostName == hostname); + resumeTimeTrackingNotification = pkgs.writeShellScript "resume-time-tracking-notification" '' + export PATH=${pkgs.lib.makeBinPath [pkgs.timewarrior pkgs.libnotify]} + task="$1" + date="$2" + if [ $(notify-send --action 'default=Resume time tracking' "Tracking '$task' stopped at $date, resume?") = "default" ]; then + timew continue + fi + ''; + lockScreen = pkgs.writeShellScript "lock-screen" '' + export PATH="${pkgs.lib.makeBinPath [pkgs.gnused pkgs.timewarrior pkgs.coreutils pkgs.swaylock]}" + task="$(timew | sed -n -r 's/^Tracking (.*)$/\1/p')" + date="$(date --rfc-3339=seconds)" + if [ "$task" != "" ]; then + timew stop + nohup ${resumeTimeTrackingNotification} "$task" "$date" >/dev/null 2>&1 & + fi + swaylock -f -i ${wallpaper} + ''; in { imports = lib.optionals nixosConfig.jalr.gui.enable [ ./gammastep.nix + ./mako.nix + ./screenshare.nix ./waybar.nix + ./wofi-bluetooth.nix ./wofi.nix ./yubikey-touch-detector.nix ]; } // (lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ + gsettings + libnotify # notify-send + mako + slurp + swappy # screenshot editing sway-contrib.grimshot # screenshots + timewarrior wdisplays # graphical output manager + wl-clipboard + wl-mirror ]; home.sessionVariables = { @@ -50,28 +88,6 @@ in _JAVA_AWT_WM_NONREPARENTING = "1"; }; - #home.sessionVariables = { - # CLUTTER_BACKEND = "wayland"; - # GDK_BACKEND = "wayland"; - # GDK_DPI_SCALE = 1; - # MOZ_ENABLE_WAYLAND = 1; - # QT_QPA_PLATFORM = "wayland-egl"; - # QT_WAYLAND_DISABLE_WINDOWDECORATION = 1; - # SDL_VIDEODRIVER = "wayland"; - # WLR_NO_HARDWARE_CURSORS = 1; - # _JAVA_AWT_WM_NONREPARENTING = 1; - # _JAVA_OPTIONS = "-Dawt.useSystemAAFontSettings=on"; - #}; - - programs.fish.loginShellInit = '' - if [ -z $WAYLAND_DISPLAY ] && [ (tty) = /dev/tty1 ] - export XDG_SESSION_TYPE="wayland" # otherwise set to tty - set -e __HM_SESS_VARS_SOURCED - set -e __NIXOS_SET_ENVIRONMENT_DONE - exec systemd-cat -t sway sway - end - ''; - wayland.windowManager.sway = { enable = true; @@ -83,19 +99,19 @@ in down = "r"; terminal = "${terminalEmulator}"; - menu = "${pkgs.wofi}/bin/wofi --allow-images --show drun"; + menu = "${pkgs.wofi}/bin/wofi --allow-images --show drun --color=$HOME/.config/wofi/color"; - output."*".bg = "${wallpaper} fill"; + input."type:keyboard" = { + xkb_layout = "de,de,us"; + xkb_variant = "neo,,"; + xkb_options = "grp:win_space_toggle"; + }; - # FIXME - #input = { - # #"type:keyboard" = { - # # xkb_layout = "neo"; - # #}; - #} // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { - # "type:touchpad".events = "disabled"; - # "2:10:TPPS/2_Elan_TrackPoint".pointer_accel = "-0.15"; - #}); + output = { + "*".bg = "${wallpaper} fill"; + } // matchHostname "copper" { + eDP-1.scale = toString 1.5; + }; keybindings = { "${cfg.modifier}+Return" = "exec ${cfg.terminal}"; @@ -217,7 +233,8 @@ in "XF86AudioMute" = "exec pactl set-source-mute alsa_input.usb-BEHRINGER_UMC202HD_192k-00.HiFi__umc202hd_mono_in_U192k_0_1__source toggle"; - "${cfg.modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}"; + "${cfg.modifier}+l" = "exec ${lockScreen}"; + "${cfg.modifier}+v" = "exec GSK_RENDERER=cairo GTK_USE_PORTAL=0 ${pkgs.mixxc}/bin/mixxc -A"; }; bars = [ ]; # managed as systemd user unit @@ -255,6 +272,20 @@ in criteria = { app_id = "firefox"; title = "Firefox — Sharing Indicator"; }; command = "kill"; } + { + criteria = { + app_id = "firefox-esr"; + title = "Extension: \\\\(Tree Style Tab\\\\) - Close tabs\\\\? — Mozilla Firefox"; + }; + command = "floating enable"; + } + { + criteria = { + app_id = "yad"; + title = "Pomodoro"; + }; + command = "floating enable"; + } ]; window.border = 2; @@ -268,45 +299,22 @@ in border = 1; }; - colors = { - focused = rec { - border = solarized.base1.hex; - background = solarized.base2.hex; - text = solarized.base1.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - focusedInactive = rec { - border = solarized.base0.hex; - background = solarized.base03.hex; - text = solarized.base0.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - unfocused = rec { - border = solarized.base0.hex; - background = solarized.base03.hex; - text = solarized.base0.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - urgent = rec { - border = solarized.base02.hex; - background = solarized.red.hex; - text = solarized.base02.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - }; - fonts = { names = [ "monospace" ]; style = "Regular"; - size = 10.0; + + # FIXME: this is an ugly workaround until https://github.com/swaywm/sway/issues/7409 is fixed + size = 0.001; }; }; extraConfig = '' + include ~/.config/sway/theme + + # Hide title bar, see https://github.com/swaywm/sway/issues/6946 + titlebar_border_thickness 0 + titlebar_padding 1 + # Cursor seat seat0 xcursor_theme Adwaita '' + ( @@ -347,35 +355,48 @@ in Environment = "PATH=${pkgs.bash}/bin:${config.wayland.windowManager.sway.package}/bin"; ExecStart = '' ${pkgs.swayidle}/bin/swayidle -w \ - timeout 300 "${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}" \ - timeout 300 '${pkgs.sway}/bin/swaymsg "output * dpms off"' \ + timeout 300 "${lockScreen}" \ + timeout 270 '${pkgs.sway}/bin/swaymsg "output * dpms off"' \ resume '${pkgs.sway}/bin/swaymsg "output * dpms on"' \ - before-sleep "${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}" + before-sleep "${lockScreen}" ''; Restart = "on-failure"; }; }; - xdg.configFile."swaynag/config".text = + xdg.configFile = let - # adding it to the header doesn’t work since the defaults overwrite it - commonConfig = /* ini */ '' - background=${lib.substring 1 6 solarized.base3.hex} - border-bottom=${lib.substring 1 6 solarized.base2.hex} - border=${lib.substring 1 6 solarized.base2.hex} - button-background=${lib.substring 1 6 solarized.base3.hex} - button-text=${lib.substring 1 6 solarized.base00.hex} + makeTheme = scheme: '' + client.focused ${scheme.base05} ${scheme.base0D} ${scheme.base00} ${scheme.base0D} ${scheme.base0D} + client.focused_inactive ${scheme.base01} ${scheme.base01} ${scheme.base05} ${scheme.base03} ${scheme.base01} + client.unfocused ${scheme.base01} ${scheme.base00} ${scheme.base05} ${scheme.base01} ${scheme.base01} + client.urgent ${scheme.base08} ${scheme.base08} ${scheme.base00} ${scheme.base08} ${scheme.base08} ''; in - /* ini */ '' - font=Monospace 12 + { + "sway/light-theme".text = makeTheme solarized.light.hex; + "sway/dark-theme".text = makeTheme solarized.dark.hex; + "swaynag/config".text = + let + # adding it to the header doesn’t work since the defaults overwrite it + commonConfig = /* ini */ '' + background=${lib.substring 1 6 solarized.colors.base3} + border-bottom=${lib.substring 1 6 solarized.colors.base2} + border=${lib.substring 1 6 solarized.colors.base2} + button-background=${lib.substring 1 6 solarized.colors.base3} + button-text=${lib.substring 1 6 solarized.colors.base00} + ''; + in + /* ini */ '' + font=Monospace 12 - [warning] - text=${lib.substring 1 6 solarized.yellow.hex} - ${commonConfig} + [warning] + text=${lib.substring 1 6 solarized.colors.yellow} + ${commonConfig} - [error] - text=${lib.substring 1 6 solarized.red.hex} - ${commonConfig} - ''; + [error] + text=${lib.substring 1 6 solarized.colors.red} + ${commonConfig} + ''; + }; }) diff --git a/home-manager/modules/sway/gammastep.nix b/users/jalr/modules/sway/gammastep.nix similarity index 100% rename from home-manager/modules/sway/gammastep.nix rename to users/jalr/modules/sway/gammastep.nix diff --git a/users/jalr/modules/sway/mako.nix b/users/jalr/modules/sway/mako.nix new file mode 100644 index 0000000..767e6b5 --- /dev/null +++ b/users/jalr/modules/sway/mako.nix @@ -0,0 +1,16 @@ +{ pkgs, ... }: + +{ + xdg.configFile."mako/config".text = '' + [mode=dnd] + invisible=1 + + [mode=screencast] + icons=0 + format=%a + + [app-name="Pomodoro timer"] + invisible=0 + icon-path="${pkgs.pomodoro-timer}/share/icons/hicolor" + ''; +} diff --git a/home-manager/modules/sway/move-to-output/default.nix b/users/jalr/modules/sway/move-to-output/default.nix similarity index 93% rename from home-manager/modules/sway/move-to-output/default.nix rename to users/jalr/modules/sway/move-to-output/default.nix index a85a425..0daabfe 100644 --- a/home-manager/modules/sway/move-to-output/default.nix +++ b/users/jalr/modules/sway/move-to-output/default.nix @@ -1,5 +1,5 @@ -{ lib, stdenv, pkgs, writeShellScript, ... }: -stdenv.mkDerivation rec { +{ stdenv, pkgs, ... }: +stdenv.mkDerivation { name = "sway-move-to-output"; phases = "installPhase"; installPhase = '' @@ -8,7 +8,7 @@ stdenv.mkDerivation rec { chmod +x $out/bin/move-to-output ''; script = '' - #!${pkgs.python310}/bin/python + #!${pkgs.python3}/bin/python import sys import json import subprocess diff --git a/users/jalr/modules/sway/screenshare.nix b/users/jalr/modules/sway/screenshare.nix new file mode 100644 index 0000000..c5a21d6 --- /dev/null +++ b/users/jalr/modules/sway/screenshare.nix @@ -0,0 +1,55 @@ +{ pkgs, ... }: +{ + home.packages = [ + ( + pkgs.writeShellScriptBin "screenshare" '' + # https://github.com/emersion/xdg-desktop-portal-wlr/issues/107 + + case "$1" in + start) + # Step 1: Create a new output + swaymsg create_output + + # Step 2: Find the name of the newly created output + NEW_OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name' | sort | tail -n 1) + + # Check if the output was successfully created + if [ -z "$NEW_OUTPUT" ]; then + echo "Failed to create a new output." + exit 1 + fi + + # Step 3: Assign a workspace to the new output + swaymsg workspace sshr output "$NEW_OUTPUT" + + # Step 4: Set the resolution for the new output + swaymsg output "$NEW_OUTPUT" resolution 1280x720 + + # Step 5: Set the background color for the new output + swaymsg output "$NEW_OUTPUT" bg "#220900" solid_color + + # Step 6: Switch to workspace sshr and then back to the previous workspace + CURRENT_WORKSPACE=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused) | .name') + swaymsg workspace sshr + swaymsg workspace "$CURRENT_WORKSPACE" + ;; + stop) + headless_outputs=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name') + + # Check if there are any HEADLESS outputs + if [ -z "$headless_outputs" ]; then + echo "No HEADLESS outputs found." + exit 0 + fi + + # Unplug each HEADLESS output + for output in $headless_outputs; do + echo "Unplugging $output..." + swaymsg output "$output" unplug + done + ;; + esac + '' + ) + ]; +} diff --git a/users/jalr/modules/sway/waybar.nix b/users/jalr/modules/sway/waybar.nix new file mode 100644 index 0000000..c507ce9 --- /dev/null +++ b/users/jalr/modules/sway/waybar.nix @@ -0,0 +1,447 @@ +{ config, lib, nixosConfig, pkgs, ... }: +let + watchUserUnitState = unit: started: stopped: pkgs.writeShellScript "watch-user-unit-${unit}-state" '' + ${pkgs.systemd}/bin/journalctl --user -u ${unit} -t systemd -o cat -f \ + | ${pkgs.gnugrep}/bin/grep --line-buffered -Eo '^(Started|Stopped)' \ + | ${pkgs.jq}/bin/jq --unbuffered -Rc 'if . == "Started" then ${builtins.toJSON started} else ${builtins.toJSON stopped} end' + ''; + + toggleUserUnitState = unit: pkgs.writeShellScript "toggle-user-unit-${unit}-state" '' + if ${pkgs.systemd}/bin/systemctl --user show ${unit} | ${pkgs.gnugrep}/bin/grep -q ActiveState=active; then + ${pkgs.systemd}/bin/systemctl --user stop ${unit} + else + ${pkgs.systemd}/bin/systemctl --user start ${unit} + fi + ''; + + # for fine-grained control over spacing + thinsp = " "; + + solarized = import ../solarized.nix; + solarizedColors = lib.attrsets.mapAttrsToList (name: color: "@define-color ${name} ${color};"); + themeCss = { + light = lib.strings.concatLines (solarizedColors solarized.light.hex); + dark = lib.strings.concatLines (solarizedColors solarized.dark.hex); + }; +in +{ + # home-manager’s waybar module performs additional checks that are overly strict + xdg.configFile."waybar/config".text = + let + makoctl = "${pkgs.mako}/bin/makoctl"; + sendSignal = signal: "${pkgs.procps}/bin/pkill -f -SIGRTMIN+${toString signal} waybar"; + in + lib.generators.toJSON { } { + layer = "top"; + output = [ + "!HEADLESS-1" + "*" + ]; + position = "top"; + height = 24; + + modules-center = [ ]; + modules-left = [ + "sway/workspaces" + "sway/mode" + ]; + modules-right = [ + "tray" + "custom/screencast" + "custom/redshift" + "idle_inhibitor" + "custom/pomodoro" + "backlight" + "pulseaudio" + "network" + "memory" + "cpu" + "temperature" + "battery" + "clock" + "custom/dnd" + ]; + + "sway/workspaces" = { + disable-scroll = true; + }; + "sway/mode" = { + format = "{}"; + }; + + tray = { + spacing = 5; + }; + "custom/redshift" = { + exec = watchUserUnitState + "gammastep" + { class = "active"; } + { class = "inactive"; }; + on-click = toggleUserUnitState "gammastep"; + return-type = "json"; + format = "󰌵"; + tooltip = false; + }; + "custom/pomodoro" = let uairctl = "${pkgs.uair}/bin/uairctl"; in { + # We need to remove nul-characters + # See https://github.com/metent/uair/issues/15 + exec = pkgs.writeShellScript "uairctl-without-null-characters" '' + ${uairctl} listen -o waybar | ${pkgs.gnused}/bin/sed --unbuffered 's/\x0//g' + ''; + + on-click = "${uairctl} toggle"; + on-scroll-up = "${uairctl} next"; + on-scroll-down = "${uairctl} prev"; + on-click-middle = "${uairctl} finish"; + menu = "on-click-right"; + /* + menu-actions = { + "work 1" = "${uairctl} jump 0"; + "break 1" = "${uairctl} jump 1"; + "work 2" = "${uairctl} jump 2"; + "break 2" = "${uairctl} jump 3"; + "work 3" = "${uairctl} jump 4"; + "break 3" = "${uairctl} jump 5"; + "work 4" = "${uairctl} jump 6"; + "long break" = "${uairctl} jump 7"; + }; + */ + return-type = "json"; + }; + idle_inhibitor = { + format = "{icon}"; + format-icons = { + activated = "󰈈 "; + deactivated = "󰈉 "; + }; + }; + "custom/screencast" = { + exec = pkgs.writeScript "screencast-monitor" /* python */ '' + #!${pkgs.python3}/bin/python3 + import subprocess + import sys + + active_outputs = 0 + + with subprocess.Popen( + ["${pkgs.coreutils}/bin/stdbuf", "-o0", "${nixosConfig.services.pipewire.package}/bin/pw-link", "-m", "-o", "xdg-desktop-portal-wlr"], + stdout=subprocess.PIPE, + text=True, + ) as proc: + for line in proc.stdout: + action = line.split(" ")[0] + if action == "=" or action == "+": + active_outputs += 1 + elif action == "-": + active_outputs -= 1 + else: + print(f"Invalid action {action} (in line {line})", file=sys.stderr) + + if active_outputs > 0: + print("󱒃") + subprocess.run(["${makoctl}", "mode", "-a" "screencast"], stdout=subprocess.DEVNULL) + else: + print() + subprocess.run(["${makoctl}", "mode", "-r" "screencast"], stdout=subprocess.DEVNULL) + + sys.stdout.flush() + ''; + format = "{}"; + tooltip = false; + }; + backlight = { + format = "{percent}% {icon}"; + format-icons = [ "󰛩" "󱩎" "󱩏" "󱩐" "󱩑" "󱩒" "󱩓" "󱩔" "󱩕" "󱩖" "󰛨" ]; + on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl -q set +1%"; + on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl -q set 1%-"; + }; + pulseaudio = { + format = "{volume}% {icon} {format_source}"; + format-bluetooth = "{volume}% {icon}󰗾{format_source}"; + format-bluetooth-muted = "{icon}󰗿{format_source}"; + format-muted = "󰝟 {format_source}"; + format-source = "{volume}% ${thinsp}"; + format-source-muted = "${thinsp}"; + format-icons = { + car = "󰄋 "; + default = [ "󰕿" "󰖀" "󰕾" ]; + hands-free = "󰋎"; + headphone = "󰋋"; + headset = "󰋎"; + phone = "󰏲"; + portable = "󰏲"; + }; + on-click-right = "GSK_RENDERER=cairo GTK_USE_PORTAL=0 ${pkgs.mixxc}/bin/mixxc -A -a t -a r"; + }; + network = { + format-wifi = "{essid} ({signalStrength}%) 󰖩 "; + format-ethernet = "{ipaddr}/{cidr} 󰈀 "; + format-linked = "{ifname} (No IP) 󰈀 "; + format-disconnected = "Disconnected ⚠ "; + format-alt = "{ifname}: {ipaddr}/{cidr}"; + tooltip = false; + on-click-right = "${config.programs.wezterm.package}/bin/wezterm start -e ${pkgs.networkmanager}/bin/nmtui"; + }; + memory = { + interval = 2; + format = "{:2}% 󰍛 "; + }; + cpu = { + interval = 2; + format = "{usage:2}%  "; + tooltip = false; + }; + temperature = { + critical-threshold = 80; + format = "{temperatureC}°C {icon}"; + format-icons = [ "" "" "" "" "" ]; + } // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { + hwmon-path = "/sys/class/hwmon/hwmon3/temp1_input"; + }); + battery = { + interval = 5; + format = "{capacity}% {icon}"; + format-charging = "{capacity}% "; + format-plugged = "{capacity}% x"; + format-alt = "{time} {icon}"; + format-icons = [ "󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; + states = { + critical = 15; + good = 95; + warning = 30; + }; + }; + clock = { + format = "{:%H:%M %Z}"; + format-alt = "{:%Y-%m-%d (%a)}"; + tooltip-format = "{:%Y %B}\n{calendar}"; + }; + "custom/dnd" = + let + signal = 1; + dndScript = "/etc/profiles/per-user/jalr/bin/dnd"; + in + { + inherit signal; + exec = pkgs.writeScript "waybar-dnd-widget" '' + #!${pkgs.python3}/bin/python3 + + import subprocess + import sys + import time + import json + + while True: + proc = subprocess.Popen(["${makoctl}", "mode"], stdout=subprocess.PIPE, text=True) + modes = [line.rstrip() for line in proc.stdout.readlines()] + + text, class_name = ("󰂛", "active") if 'dnd' in modes else ("󰂚", "inactive") + + print(json.dumps({ + "text": text, + "tooltip": "", + "class": class_name, + "percentage": 0 + })) + + sys.stdout.flush() + + time.sleep(30) + ''; + on-click = "${dndScript} on; ${sendSignal signal}"; + on-click-right = "${dndScript} off; ${sendSignal signal}"; + exec-on-event = true; + return-type = "json"; + restart-interval = 10; + }; + }; + + xdg.configFile = { + "waybar/theme-light.css".text = themeCss.light; + "waybar/theme-dark.css".text = themeCss.dark; + "waybar/style.css".text = '' + @import "theme.css"; + + * { + border-radius: 0; + border: none; + font-family: "Iosevka Nerd Font"; + font-size: 14px; + min-height: 0; + transition-property: none; + } + + window#waybar { + background-color: @base00; + color: @base04; + } + + #workspaces button { + padding: 0 5px; + background-color: @base00; + color: inherit; + border-bottom: 2px solid transparent; + } + + #workspaces button:hover { + background: @base01; + box-shadow: inherit; + text-shadow: inherit; + } + + #workspaces button.focused { + border-bottom: 2px solid @base0B; + } + + #workspaces button.urgent { + background-color: @base08; + } + + #mode { + background-color: @base01; + font-style: italic; + } + + /* all modules on the right */ + #waybar > box > box:nth-child(3) > widget > label { + padding: 0 10px; + } + + #battery.charging { + color: @base01; + background-color: @base0B; + } + + @keyframes blink { + to { + background-color: @base07; + color: @base03; + } + } + + #battery.critical:not(.charging), + #temperature.critical { + background-color: @base08; + animation-name: blink; + animation-duration: 0.5s; + /* FIXME use nearest neighbor interpolation if possible */ + animation-timing-function: cubic-bezier(1, 0, 0, 1); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #cpu { + background-color: @base0C; + color: @base01 + } + + #memory { + background-color: @base0A; + color: @base01 + } + + #backlight { + background-color: @base07; + color: @base03; + } + + #network { + background-color: @base0E; + color: @base01 + } + + #network.disconnected { + background-color: @base08; + } + + #pulseaudio { + background-color: @base07; + color: @base03; + } + + #pulseaudio.muted { + background-color: @base00; + color: @base04; + } + + #temperature { + background-color: @base0F; + color: @base01; + } + + #idle_inhibitor.activated { + background-color: @base07; + color: @base00; + } + + #custom-redshift { + color: @base01; + } + + #custom-redshift.active { + background-color: @base08; + } + + #custom-redshift.inactive { + background-color: @base0D; + } + + #tray { + padding: 0 5px; + } + + #custom-notification_inhibitor.active { + background-color: @base07; + color: @base00; + } + + #custom-screencast { + background-color: @base08; + color: @base00; + animation-name: blink; + animation-duration: 1s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #custom-pomodoro.resumed { + color: @base01; + background-color: @base0B; + } + + #custom-dnd.active { + color: @base01; + background-color: @base0B; + } + ''; + }; + + systemd.user.services.waybar = { + Unit = { + Description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; + Documentation = "https://github.com/Alexays/Waybar/wiki/"; + PartOf = [ "sway-session.target" ]; + }; + + Install.WantedBy = [ "sway-session.target" ]; + + Service = { + # ensure sway is already started, otherwise workspaces will not work + ExecStartPre = "${config.wayland.windowManager.sway.package}/bin/swaymsg"; + ExecStart = "${pkgs.waybar}/bin/waybar"; + ExecReload = "${pkgs.utillinux}/bin/kill -SIGUSR2 $MAINPID"; + Restart = "on-failure"; + RestartSec = "1s"; + }; + }; + + # TODO: remove when https://github.com/nix-community/home-manager/issues/2064 + # is resolved + systemd.user.targets.tray = { + Unit = { + Description = "Home Manager System Tray"; + Requires = [ "graphical-session-pre.target" ]; + }; + }; +} diff --git a/users/jalr/modules/sway/wofi-bluetooth.nix b/users/jalr/modules/sway/wofi-bluetooth.nix new file mode 100644 index 0000000..76d2d2b --- /dev/null +++ b/users/jalr/modules/sway/wofi-bluetooth.nix @@ -0,0 +1,5 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.bluetooth.enable { + home.packages = [ pkgs.wofi-bluetooth ]; +} diff --git a/users/jalr/modules/sway/wofi.nix b/users/jalr/modules/sway/wofi.nix new file mode 100644 index 0000000..44d2914 --- /dev/null +++ b/users/jalr/modules/sway/wofi.nix @@ -0,0 +1,92 @@ +{ lib, ... }: + +let + inherit (import ../solarized.nix) colors; +in +{ + xdg.configFile = + let + commonColors = with colors; [ + red + orange + yellow + green + cyan + blue + violet + magenta + ]; + in + { + "wofi/color-light".text = with colors; lib.strings.concatLines ( + [ + base3 + base2 + base1 + base0 + base00 + base01 + base02 + base03 + ] ++ commonColors + ); + "wofi/color-dark".text = with colors; lib.strings.concatLines ( + with colors; [ + base03 + base02 + base01 + base00 + base0 + base1 + base2 + base3 + ] ++ commonColors + ); + "wofi/style.css".text = '' + window { + margin: 0px; + border: 3px solid --wofi-color1; + border-radius: 8px; + background-color: rgba(--wofi-rgb-color0,0.8); + } + + #input { + margin: 5px; + border: none; + color: --wofi-color4; + background-color: rgba(--wofi-rgb-color1,0.8); + } + + #inner-box { + margin: 5px; + border: none; + background: none; + } + + #outer-box { + margin: 5px; + border: none; + background: none; + } + + #scroll { + margin: 0px; + border: none; + } + + #text { + margin: 5px; + border: none; + color: --wofi-color4; + } + + #entry:selected { + background-color: rgba(--wofi-rgb-color1,0.8); + } + + #entry:selected #text{ + color: --wofi-color11; + } + ''; + }; +} diff --git a/home-manager/modules/sway/yubikey-touch-detector.nix b/users/jalr/modules/sway/yubikey-touch-detector.nix similarity index 95% rename from home-manager/modules/sway/yubikey-touch-detector.nix rename to users/jalr/modules/sway/yubikey-touch-detector.nix index 658f653..0d01b60 100644 --- a/home-manager/modules/sway/yubikey-touch-detector.nix +++ b/users/jalr/modules/sway/yubikey-touch-detector.nix @@ -1,4 +1,4 @@ -{ nixosConfig, config, lib, pkgs, ... }: +{ pkgs, ... }: { systemd.user.services.yubikey-touch-detector = { diff --git a/home-manager/modules/pcmanfm.nix b/users/jalr/modules/thunar.nix similarity index 74% rename from home-manager/modules/pcmanfm.nix rename to users/jalr/modules/thunar.nix index 49819d8..28b5b0b 100644 --- a/home-manager/modules/pcmanfm.nix +++ b/users/jalr/modules/thunar.nix @@ -1,7 +1,8 @@ { nixosConfig, lib, pkgs, ... }: lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - pcmanfm + xfce.thunar + xfce.thunar-volman ]; } diff --git a/users/jalr/modules/thunderbird.nix b/users/jalr/modules/thunderbird.nix new file mode 100644 index 0000000..e8cd892 --- /dev/null +++ b/users/jalr/modules/thunderbird.nix @@ -0,0 +1,14 @@ +{ nixosConfig, pkgs, ... }: +{ + programs.thunderbird = { + inherit (nixosConfig.jalr.gui) enable; + package = pkgs.thunderbird-esr; + profiles."default" = { + isDefault = true; + withExternalGnupg = true; + }; + settings = { + "mail.chat.enabled" = false; + }; + }; +} diff --git a/home-manager/modules/tmux.nix b/users/jalr/modules/tmux.nix similarity index 81% rename from home-manager/modules/tmux.nix rename to users/jalr/modules/tmux.nix index a0781e4..c8d4c10 100644 --- a/home-manager/modules/tmux.nix +++ b/users/jalr/modules/tmux.nix @@ -1,4 +1,3 @@ -{ config, pkgs, ... }: { programs.tmux = { enable = true; diff --git a/users/jalr/modules/tor-browser.nix b/users/jalr/modules/tor-browser.nix new file mode 100644 index 0000000..f0420ab --- /dev/null +++ b/users/jalr/modules/tor-browser.nix @@ -0,0 +1,6 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + tor-browser-bundle-bin + ]; +} diff --git a/home-manager/modules/claws-mail.nix b/users/jalr/modules/trilium.nix similarity index 81% rename from home-manager/modules/claws-mail.nix rename to users/jalr/modules/trilium.nix index ff32d39..91b6dfe 100644 --- a/home-manager/modules/claws-mail.nix +++ b/users/jalr/modules/trilium.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - claws-mail + trilium-next-desktop ]; } diff --git a/home-manager/modules/vdirsyncer.nix b/users/jalr/modules/vdirsyncer.nix similarity index 85% rename from home-manager/modules/vdirsyncer.nix rename to users/jalr/modules/vdirsyncer.nix index c8c828d..c853db1 100644 --- a/home-manager/modules/vdirsyncer.nix +++ b/users/jalr/modules/vdirsyncer.nix @@ -45,27 +45,6 @@ let }; }; - mkWebcalSection = { name, url ? null, urlCommand ? null }: assert url == null -> urlCommand != null; { - "pair calendar_${name}" = { - a = "calendar_${name}_local"; - b = "calendar_${name}_remote"; - collections = null; - }; - - "storage calendar_${name}_local" = { - type = "filesystem"; - path = "${calendarBasePath}/${name}/"; - fileext = ".ics"; - }; - - "storage calendar_${name}_remote" = { - type = "http"; - } // (if urlCommand != null then { - "url.fetch" = fetchCommand urlCommand; - } else { - inherit url; - }); - }; in { home.packages = with pkgs; [ diff --git a/users/jalr/modules/vesc-tool.nix b/users/jalr/modules/vesc-tool.nix new file mode 100644 index 0000000..3b5d90b --- /dev/null +++ b/users/jalr/modules/vesc-tool.nix @@ -0,0 +1,12 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf (nixosConfig.jalr.gui.enable && nixosConfig.networking.hostName == "copper") { + home.packages = with pkgs; [ vesc-tool ]; + xdg.dataFile."VESC/firmware".source = pkgs.bldc-fw.override { + fwBoards = [ + "410" + "60" + "60_mk3" + ]; + }; +} diff --git a/users/jalr/modules/wezterm.nix b/users/jalr/modules/wezterm.nix new file mode 100644 index 0000000..be238eb --- /dev/null +++ b/users/jalr/modules/wezterm.nix @@ -0,0 +1,46 @@ +{ config, lib, nixosConfig, ... }: + +let + weztermConfig = { + hide_tab_bar_if_only_one_tab = true; + #color_scheme = "Solarized (dark) (terminal.sexy)"; + #color_scheme = "Solarized (light) (terminal.sexy)"; + font = "FiraCode Nerd Font Mono"; + font_size = 12; + }; +in +{ + programs.wezterm = { + inherit (nixosConfig.jalr.gui) enable; + extraConfig = '' + local wezterm = require 'wezterm' + ${lib.generators.toLua {asBindings=true;} {config=weztermConfig;}} + config.font = wezterm.font(config.font) + + function get_appearance() + if wezterm.gui then + return wezterm.gui.get_appearance() + end + return 'Light' + end + + function scheme_for_appearance(appearance) + if appearance:find 'Dark' then + return 'Builtin Solarized Dark' + else + return 'Builtin Solarized Light' + end + end + + config.color_scheme = scheme_for_appearance(get_appearance()) + + return config + ''; + }; + /* + file = io.open("${config.xdg.configHome}/wezterm/color_scheme.txt", "r") + io.input(file) + config.color_scheme = io.read() + io.close(file) + */ +}